WCF message headers with OperationContext and with MessageInspector and Custom Service Behavior
April 14, 2014 § Leave a comment
WCF message headers with OperationContext and with MessageInspector and Custom Service Behavior
In this post we will view some possible options for adding message headers to the messages send from the client to service.
We have a WCF service provided as a Software as a Service (SaaS). People who have an active subscription with our company, are able to invoke methods on our service and retrieve information. To successfully invoke methods and retrieve information the client invoking the method must add a message header named “SubscriptionID“ which contains the subscriptionid of that customer. If the subscriptionid does not match a valid and active subscriptionid, access to the operations are denied.
1. Setup and configuration of the Service
The “WCF.MessageHeaders.Client” is a Console Application used to mimic a client
The “WCF.MessageHeaders.Service” is a WCF Service Application, holding our SaasService
Our service is called SaasService and stands for our saas service that provides certain operations for information to external clients.
Our service interface looks as following:
And our service implementation as following (click to view enlarged):
We have 1 public demo method on our SaasService, namely InvokeMethod() with returns a string (or any kind of information). When invoking the method we check if we can find a header called “SubscriptionID” and an empty namespace (for the demo purpose we used an empty namespace, less hassle). If the message header is not found in the incoming message headers, access is denied. If the SubscriptionID message header is found, it is validated against the subscription store to validate it and if it a valid subscriptionid, the required information is returned to the client.
Getting Message headers at the service side can be found at OperationContext.Current.IncommingMessageHeaders. The method FindHeader allows you to check if the header exists and the method GetHeader allows you to retrieve the header information.
Our web.config looks as following:
Nothing special, we just use a basicHttpBinding and we leave the address unspecified as we will host this service in our local IIS. I’m no fan of hosting services in managed applications, unless it’s really necessary.
To host the service easily in IIS, go to the service project properties, go to tab “Web” and instead of using the Visual Studio Development Server use local IIS Web Server and press the “Create Virtual Directory”
Your service should be available at:
2. Setting up and configuring the client
We simply add a service reference to the service url mentioned above.
This is the code we put at our client console application:
If we run our client console application:
Behaves as intended, as we didn’t add any serial key at the outgoing messages at our client, there is no subscriptionID message header found at the message at service, so the access is denied. If we are a valid customer, we have a subscriptionID which we can pass on to the service, which will grant us access.
The code should look as following:
Basically we start by creating the WCF client proxy, which will invoke the operation. Then we create a new OperationContextScope, based on the proxy’s innerChannel. The new OperationContextScope at the client has to be created, before you can access OperationContext.Current.outgoingMessageHeaders, which we need to add a message header to the outgoing messages. If you do not use the operationcontextscope, the OperationContext will be nothing and an error will occur will you try to invoke OperationContext.Current.outgoingMessageHeaders.
Next we create a MessageHeader of type string with the subscriptionID value passed in the message header. This is the subscriptionID which should grant us access to the SaasService. After that we can invoke messageheader.GetUntypedHeader(string name, string ns) which allows us to get an untyped message header, with a defined message header name and namespace. In our case the name is “SubscriptionID”, which is the name of the messageheader the service looks for. Our namespace in this demo is just empty.
After having created the message header, we can simply add it to the outgoingmessageheaders and invoke the method on the proxy.
The result, which we expect:
All by all, adding message headers to incoming or outgoing messages and validating them is not that difficult. However when we have a service with a few tens or hundreds of operations, we do not want to validate the message header at every operation. The solution for that is using a message inspector and a custom service behavior.
3. Use a messageInspector and add a custom Behaviour to our service to validate all incoming messages
We have a SaasService which will expose a few hundreds of operations, for example.
For our first method we used quite some code, to find the header, get the header and validate the header content if it is a valid subscriptionID.
However if I have to write this exact code 99 times more for the other 99 methods I need to implement, that would be silly. We could put the message header validation code in 1 method and in every operation then call that validation method which does the validation and for example returns a boolean. That would save us from duplicating our validation code, however it still requires us to have 100 methods which call all the same validation method and the operation only executes on the result returned of that validation method.
Another possibility is to add a message inspector, which inspects the incoming message before it is handed off to the service operation. At the message inspecor we inspect the message headers to check for the subscriptionid and do some validation if it is provided. If it is not provided or an incorrect subscriptionid is provided, we return an error that the service consumer does not have access to the service. It will save us from writing any validation code at all at our service. We can simply write operations and assume that if the operation is invoked, the client invoking the operation has valid access. Quite a nice solution if you ask me.
How do we implement this:
1. Create an IDispatchMessageInspector at the service side
We start by creating a class “SaasServiceMessageInspector” which is the class which will inspect each message send to our SaasService, inspecting if the subscriptionid message header is present and validate it.
To create a message inspector at the service side, we need to inherit from “IDispatchMessageInspector” which is an interface provided to create message inspectors at service side. The method we need to implement, in our case, is the AfterReceiveRequest, which gives us access to the message after it has been received.
The implementation (click the enlarge):
The message is being passed in the AfterReceiveRequest method, so we can just use the request.Headers to find if our SubscriptionID header is present and whether a valid subscriptionid is provided. If no subscriptionid or no valid subscriptionid is provided, we return a faultexception to the client notifying them that access to the service is denied.
Instead of validating the subscriptionid’s against a database or any store, we just check if the subscriptionid matches “123-456789-098″. If it does not match this subscriptionid, the client should be denied access to the service, even before any method gets invoked. An ideal scenario to easily test our code in a demo.
Now after we implemented the IDispatchMessageInspector we need to write some code that this IDispatchMessageInspector is linked to our SaasService and that this message inspection happens at service level. Any operation that might get invoked of our service, needs to have the message inspected first by the message inspector.
2. Create an IServiceBehavior and attach the custom behavior by attribute
To attach our new SaasServiceMessageInspector to our SaasService, we need to create a new behavior (like which we define in our web.config for example).
There are multiple behaviors we can extend:
- IServiceBehavior: Applies to the entire service
- IEndpointBehavior: Applies to a specific endpoint
- IContractBehavior: Applies to a specific contract
- IOperationBehavior : Applies to a specific operation
For each channeldispatcher we get each endpointdispatcher and add a SaasServiceMessageInspector to the dispatchruntime message inspectors.
Notice that we also inherit from the Attribute, which makes our SaasServiceMessageInspectorBehavior to be used as an attribute.
Also make sure you have System.ServiceModel.Dispatcher namespace imported for the code above.
We can attach our new behavior SaasServiceMessageInspectorBehavior to our Service as following:
We only attach the custom behavior as as attribute on our service, and the custom behavior is automatically attached to our SaasService and we have a message inspector attached to each endpoint dispatchruntime.
Also notice that our InvokeMethod has no more validation code at all at the method. Only method functionality is added.
Our service solution looks as following:
Our client console application remains the same. The subscriptionID used at our client console currently is:
This is the valid subscriptionID we check for in our message-inspector, so we should be able to invoke the method:
Executing the client console application:
Works as intended. If we change the subscriptionid at our client to an incorrect subscription and try to invoke a service method:
Again working as intended. So we created a custom message inspector and a custom service behavior, which did the message header validation for the entire service, only at 1 single and isolated place.
4. What about using a message inspector and custom behavior at the client to add message headers to the outgoing messages
Now we did this for the service, which is a great solution. But what about the client ? If the client has to invoke a few hundreds or thousands of methods of the SaasService, it has to create an OperationScope, create a message header and attach it to the messageheaders, before invoking the operation. (yes you could create only 1 proxy, attach the message header there and always return that proxy to use for invoking operations). Well it is possible to create the same behavior on the client side. Create a message inspector who adds a message header to the request, instead of checking for it and create a custom behavior to attach to your client proxy.
I will not run into all code by detail again, as it resembles the previous section a lot.
We start by creating our message inspector at the client side (click to view at full size):
Notice that we inherit from the IClientMessageInspector now, and not from the IDispatchMessageInspector! The IDispatch interfaces are for service side, while the IClient interfaces are for the client side.
We override the BeforeSendRequest method, which is invoked before the messages is handed over to the channel which will route the message to the service. In this method we create the message header and attach it to our request.
Next we create a custom behavior, just as in the previous section for the service side. Now we will inherit from IEndpointBehavior and not from IServiceBehavior:
The IServiceBehavior is for the service side, so not applicable for the client. The IEndpointBehavior is ideal as it’s scope is a specific endpoint, which in our case is the endpoint to the SaaSService. We use the ApplyClientBehavior. Notice there is also an ApplyDispatchBehavior, in case you would use this behavior for the service side. So the IEndpointBehavior can be used for aswell the client as for the service.
In the previous section we attached the behavior by Attribute to the service. However for an Endpoint behavior this is not possible, it can only be attached by configuration file. To attach custom behaviors by configuration file, we need to create an extension from BehaviorExtensionElement
You will also need to import the reference System.Configuration. Now that we created the behaviorExtension, we can add this behavior extension in our app.config:
To add our behaviorextension at our app.config, we need to add an <extensions> node under <system.serviceModel> and add a <behaviorExtensions> in which we add our custom behavior extension.
We now create an endpoint behavior, that used our MessageHeaderInspector extension:
Notice that in the behavior we use the “MessageHeaderInspector” which is defined at our behaviorextensions with name “MessageHeaderInspector”.
We now attach this endpointbehavior to our client endpoint:
This way our endpoint is linked to the endpoint behavior “SaasEndpointBehavior”, which is our endpoint behavior using the MessageHeaderInspector, which is our custom code to add a subscriptionid message header to each outgoing message for the endpoint we defined.
Our client console code looks as following:
So we do not add the message header anymore in our code. Our message inspector and custom behavior will take care of this, for every operation we invoke with your proxy on the endpoint defined.
Our solutions looks like this now:
When executing our client console application: