2

I'm trying to add a custom header to every outgoing request message via WCF.

I created the header object like that:

EndpointAddressBuilder eab = new EndpointAddressBuilder(combinedService.Endpoint.Address);
AddressHeader addressHeader = AddressHeader.CreateAddressHeader("HeaderData", String.Empty, "String data");
eab.Headers.Add(addressHeader);
combinedService.Endpoint.Address = eab.ToEndpointAddress();

I use this exact code in two positions in my code, one works well but the other does not. The problem is in the following line of code:

AddressHeader addressHeader = AddressHeader.CreateAddressHeader("HeaderData", String.Empty, "String data");

When it works (pass the header data successfully) the created object looks like that: enter image description here

But when it doesn't work, the created object looks like that: enter image description here

The exact same method does this but two position code calls yield different results.

Is there any method I should execute on the addressHeader object to force it serialize the object? Maybe something like: Flush()?

I KNOW I can use several well known patterns of adding a custom headers like: "custom behavior", "Client Message Inspector" etc... But I have a requirement to add it on a specific point right before we send the message.

Jacob
  • 3,598
  • 4
  • 35
  • 56

1 Answers1

1

I've, finally, got a solution.

I just followed the steps in this and this excellent, detailed and simple articles.

Although this code might seems to be long and complecated, this is the most right manner for handling header data in WCF. So it'll finally worth it.

You just have to configure custom behavior for handling WCF header.

Here is how it goes:

Client Side:

public class FillHeaderDataBehaviourExtension : BehaviorExtensionElement, IEndpointBehavior
{
    #region BehaviorExtensionElement Implementation
    public override Type BehaviorType
    {
        get
        {
            return typeof(FillHeaderDataBehaviourExtension);
        }
    }
    protected override object CreateBehavior()
    {
        return this;
    }
    #endregion

    #region IServiceBehaviour Implementation
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {

    }
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(new MessageInspector());
    }
    #endregion
}

public class MessageInspector : IClientMessageInspector
{
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        MessageHeader header = MessageHeader.CreateHeader("HeaderData", String.Empty, HeaderDataVM.GetInstance().GetBaseInstance());
        request.Headers.Add(header); // There is no need for checking if exist before adding. Every request has it's own headers.

        return null;
    }
    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
    }
}

Server Side:

public class ExtractHeadersBehaviourExtension : BehaviorExtensionElement, IServiceBehavior
{
    #region BehaviorExtensionElement Implementation
    public override Type BehaviorType
    {
        get
        {
            return typeof(ExtractHeadersBehaviourExtension);
        }
    }
    protected override object CreateBehavior()
    {
        return this;
    }
    #endregion

    #region IServiceBehavior Implementation
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
        {
            ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
            if (channelDispatcher != null)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    MessageInspector inspector = new MessageInspector();
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
                }
            }
        }
    }
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
    #endregion
}

public class MessageInspector : IDispatchMessageInspector
{
    public void BeforeSendReply(ref Message reply, object correlationState)
    {
    }
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        HeaderData headerData = request.Headers.GetHeader<HeaderData>("HeaderData", String.Empty);

        if(headerData != null)
        {
            OperationContext.Current.IncomingMessageProperties.Add("HeaderData", headerData);
        }

        return null;
    }
}

And finally, don't forget to configure it in the app.config files (client & server side) as follows:

<behaviors>
  <endpointBehaviors>
    <behavior name="NewBehavior">
      <fillHeaderDataBehaviourExtension/>
    </behavior>
  </endpointBehaviors>
</behaviors>
<bindings>

You can also add these lines via the WCF config editor. To do so, look at this answer.

Important: Note that you'll get an error in the app config after adding these lines of configuration code:

enter image description here

Don't worry about this, your application will run correctly. It causes because the GAC (Global Assembly Cache) folder doesn't contain this behavior (since it is custom behavior). You can fix it by adding this behavior manually to your GAC folder on your computer. However, this error might prevent you from updating service reference. If you try to, you'll get this error message:

enter image description here

So just comment out this line (<extractHeadersBehaviourExtension/>) (in client & server side) when you update your service reference.

Community
  • 1
  • 1
Jacob
  • 3,598
  • 4
  • 35
  • 56