Add Custom Message Header in WCF 4 Calls

Often, we want to pass some data to some or maybe all our service operations. This data is usually context data such as user tokens, or environmental preferences of the user or machine.

In simple web service we can pass custom header information using Attribute called “[SoapHeaderAttribute ("ServiceHeader", Direction=SoapHeaderDirection.In)]” along with Web Method signatures.

But in WCF, we cannot use the same attribute.

One way would be to pass it as an additional request parameter. But, each and every method call needs to have this parameter(s) repeatedly. Not a very clean solution. Also, if the data type of this parameter changes, all the method signatures and their calls need to be changed.

A nice and easy way to pass that data is to use Message Headers.

In WCF, to pass the custom header information along with method call, we need to implement custom inspector for client and service which will implement the
BeforeSendRequestand AfterRecieveRequestmethods to inject the custom header.

In order to do this we need following objects/classes:

SOAP Header

Message Inspector

Client Context and Server Context class

Custom Behavior

Let’s start creating these classes one by one.

1. SOAP Header

The CustomHeader class is used to create custom header for service in which we want to pass header information along with method call.
The CustomHeader class contains the information that we want to pass along with method call. You can define the structure as per your needs.

As you can see it is a type inheriting from MessageHeader class. Notice the
OnWriteHeaderContents override, which is invoked by WCF infrastructure to serialize the SOAP Header, and the
ReadHeader static method that we will use later.

2. Message Inspector

SOAP Header needs to be added by the consumer and read by the service. To do this we need a Message Inspector like the following one:

///<summary>/// This class is used to inspect the message and headers on the server side,
/// This class is also used to intercept the message on the
/// client side, before/after any request is made to the server.
///</summary>publicclass CustomMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
#region Message Inspector of the Service
///<summary>/// This method is called on the server when a request is received from the client.
///</summary>///<paramname="request"></param>///<paramname="channel"></param>///<paramname="instanceContext"></param>///<returns></returns>publicobject AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
{
// Create a copy of the original message so that we can mess with it.
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message messageCopy = buffer.CreateMessage();
// Read the custom context data from the headers
ServiceHeader customData = CustomHeader.ReadHeader(request);
// Add an extension to the current operation context so
// that our custom context can be easily accessed anywhere.
ServerContext customContext = new ServerContext();
if (customData != null)
{
customContext.KerberosID = customData.KerberosID;
customContext.SiteminderToken = customData.SiteminderToken;
}
OperationContext.Current.IncomingMessageProperties.Add(
"CurrentContext", customContext);
returnnull;
}
///<summary>/// This method is called after processing a method on the server side and just
/// before sending the response to the client.
///</summary>///<paramname="reply"></param>///<paramname="correlationState"></param>publicvoid BeforeSendReply(ref Message reply, object correlationState)
{
// Do some cleanup
OperationContext.Current.Extensions.Remove(ServerContext.Current);
}
#endregion#region Message Inspector of the Consumer
///<summary>/// This method will be called from the client side just before any method is called.
///</summary>///<paramname="request"></param>///<paramname="channel"></param>///<returns></returns>publicobject BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Prepare the request message copy to be modified
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
ServiceHeader customData = new ServiceHeader();
customData.KerberosID = ClientContext.KerberosID;
customData.SiteminderToken = ClientContext.SiteminderToken;
CustomHeader header = new CustomHeader(customData);
// Add the custom header to the request.
request.Headers.Add(header);
returnnull;
}
///<summary>/// This method will be called after completion of a request to the server.
///</summary>///<paramname="reply"></param>///<paramname="correlationState"></param>publicvoid AfterReceiveReply(ref Message reply, object correlationState)
{
}
#endregion
}

As you can see from the code sample above, we use the IClientMessageInspector implementation to handle the addition of the header in the consumer-side code, while we use the
IDispatchMessageInspector on the service side, to extract the header. It is interesting the
FindHeader method of the MessageHeaders collection, as well as the method
GetReaderAtHeader, provided by the same collection of Headers. The result of this last method is an
XmlDictionaryReader that we use to read our custom header content, through the
ReadHeader static method we’ve already introduced.

3. Client Context and Server Context class

The ClientContext class is used to store the header information before calling the method, so when you want to attach the custom header data, you just need to set the values for this
ClientContext class. These values get fetched inside BeforeSendRequest method of CustomMessageInspector class and send along with the request made.

///<summary>/// This class will act as a custom context in the client side to hold the context information.
///</summary>publicclass ClientContext
{
publicstaticstring EmployeeID;
publicstaticstring WindowsLogonID;
publicstaticstring KerberosID;
publicstaticstring SiteminderToken;
}

At server side, once custom header is received, it will be stored inside this
ServerContext class object, so that we can access it anytime once request is received.

Implement the IEndpointBehaviorinterface to modify, examine, or extend some aspect of endpoint-wide execution at the application level for either client or service applications.

Use the AddBindingParameters method to pass custom data at runtime to enable bindings to support custom behavior.

Use the ApplyClientBehavior method to modify, examine, or insert extensions to an endpoint in a client application.

Use the ApplyDispatchBehavior method to modify, examine, or insert extensions to endpoint-wide execution in a service application.

Use the Validate method to confirm that a ServiceEndpoint meets specific requirements. This can be used to ensure that an endpoint has a certain configuration setting enabled, supports a particular feature and other requirements.

Implement IServiceBehaviorto modify, examine, or extend some aspect of service-wide execution at the application level:

Use the ApplyDispatchBehavior method to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.

Use the Validate method to examine the description before constructs the executing service to confirm that it can execute properly.

Use the AddBindingParameters method to pass to a binding element the custom information for the service so that it can support the service correctly.

Adding Behavior to the Runtime

When you construct a ServiceHost or client-side ChannelFactory, the runtime reflects over the service types, reads the configuration file, and starts building an in-memory description of the service. Within
ServiceHost, this description is made available to you via the Description property (of type
ServiceDescription). Within ChannelFactory, it’s made available via the Endpoint property (of type
ServiceEndpoint); the client-side description is limited to the target endpoint.

The ServiceDescription contains a full description of the service and each endpoint (ServiceEndpoint), including contracts (ContractDescription) and operations (OperationDescription).
ServiceDescription provides a Behaviors property (a collection of type
IServiceBehavior) that models a collection of service behaviors. Each
ServiceEndpoint also has a Behaviors property (a collection of type
IEndpointBehavior) that models the individual endpoint behaviors. Likewise,
ContractDescription and OperationDescription each have an appropriate Behaviors property.

These behavior collections are automatically populated during the ServiceHost and
ChannelFactory construction process with any behaviors that are found in your code (via attributes) or within the configuration file (more on this shortly). You can also add behaviors to these collections manually after construction. The following example shows how to add the CustomBehavior to the host as a service behavior:

Adding Behavior with Attribute

During the ServiceHost/ChannelFactory construction process, the runtime reflects over the service types and configuration file and automatically adds any behaviors it finds to the appropriate behavior collections in the
ServiceDescription.