Client Additional Parameters using Custom Headers in WCF

When building Service communication applications using Windows Communication Foundation, we often come to a situation where we have multiple clients communicating with WCF Service and we need to pass additional parameters from client to service. This may be due to number of reasons such as sharing a unique customer ID to identify certain elements for that particular client. In such scenarios, for every communication between client and server, you need this unique ID to treat each client accordingly.

Client-Server-Customer-Id-Parameter-WCF

In a typical disconnected environment over the internet you need to pass such data on each call to service and of course passing in each Service Method or Operation Call is confusing and not appropriate.

A good Solution is to pass additional parameters in SOAP headers utilizing with the help of Custom headers in WCF. A SOAP envelope contains a header and a body. Method call and its parameters are transformed to SOAP body whereas SOAP header usually contains application-specific information (like authentication etc.)

 image

A simple way to achieve this in WCF is to add MessageHeader in your proxy class as in code snippet below:

public partial class TestServiceClient : ClientBase<Client.ServiceProxy.ITestService>,
                                         Client.ServiceProxy.ITestService
{
    public TestServiceClient()
    {
        var header = new MessageHeader<string>("Customer Unique Id: 12345");
        var untyped = header.GetUntypedHeader("Identity", "http://www.adilmughal.com");
        OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
    }
 
    //Other constructors and Service Method Calls
}

However this is really NOT a suitable approach as if you would be generating proxy using svcutil.exe or using Visual Studio then it will replace your proxy code.

A Better Solution to solve this problem is to utilize custom behavior extension in WCF to pass additional parameters in SOAP Message header. Following are the steps required on client:

1) Implement IClientMessageInspector to create custom message inspector

public class MyMessageInspector : IClientMessageInspector
{
    #region IClientMessageInspector Members
 
    public void AfterReceiveReply(ref Message reply,
                                    object correlationState)
    {
        Console.WriteLine("SOAP Response: {0}", reply.ToString());
    }
 
    public object BeforeSendRequest(ref Message request,
                                    IClientChannel channel)
    {
        var header = new MessageHeader<string>("Customer Unique Id: 12345");
        var untyped = header.GetUntypedHeader("Identity", "http://www.adilmughal.com");
        request.Headers.Add(untyped);
        Console.WriteLine("SOAP Request: {0}", request.ToString());
        return null;
    }
 
    #endregion
}

2) Create a custom behavior class implementing IEndPointBehavior and BehaviorExtensionElement. Then add the custom message inspector in runtime in ApplyClientBehavior method as shown in code snippet below:

public class CustomBehavior : BehaviorExtensionElement, IEndpointBehavior
{
    #region IEndpointBehavior Members
 
    public void AddBindingParameters(ServiceEndpoint endpoint,
                                        BindingParameterCollection bindingParameters)
    {
    }
 
    public void ApplyClientBehavior(ServiceEndpoint endpoint,
                                    ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new MyMessageInspector());
    }
 
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
                                        EndpointDispatcher endpointDispatcher)
    {
    }
 
    public void Validate(
        ServiceEndpoint endpoint)
    {
    }
 
    #endregion
 
    public override Type BehaviorType
    {
        get
        {
            return typeof(CustomBehavior);
        }
    }
 
    protected override object CreateBehavior()
    {
        return new CustomBehavior();
    }
}

3) Finally, register behavior in .config under <system.servicemodel>

<behaviors>
  <endpointBehaviors>
    <behavior>
      <customInspector />
    </behavior>
  </endpointBehaviors>
</behaviors>
<extensions>
  <behaviorExtensions>
    <add name="customInspector" type="Client.CustomBehavior, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </behaviorExtensions>
</extensions>

That is all you have to do on client end to pass additional parameter in SOAP header using Custom Behavior in WCF.

To retrieve parameter passed in header on server side, you need to use OperationContext object

MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders; 
string customParameter = headers.GetHeader<string>("Identity", "http://www.adilmughal.com");
return string.Format("You entered: {0}", customParameter);

This would achieve the objective. Following is the detail of SOAP Envelope:

SOAP Request:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/ITestService/GetData</Action>
    <Identity xmlns="http://www.adilmughal.com">Customer Unique Id: 12345</Identity>
  </s:Header>
  <s:Body>
    <GetData xmlns="http://tempuri.org/">
      <value>123</value>
    </GetData>
  </s:Body>
SOAP Response:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <GetDataResponse xmlns="http://tempuri.org/">
      <GetDataResult>You entered: Customer Unique Id: 12345</GetDataResult>
    </GetDataResponse>
  </s:Body>
</s:Envelope>

In this post we saw how to create custom SOAP header in WCF to pass parameter from client to server using WCF behavior extension. Hope this helps.

Leave a Reply

Your email address will not be published. Required fields are marked *