We recently had a requirement to add a custom http header to messages being sent on a BizTalk basicHttpBinding send port.

If you want to add SOAP headers, this is supported by the biztalk adapters. You just simply set the WCF.OutboundCustomHeaders context property on your outgoing message and they get added in.

I was expecting a similar context property to be available for the Http headers, WCF.HttpHeaders being a good candidate, or maybe Http.UserHttpHeaders.

After much testing and frustration, neither of those appeared to work.

Implementing a IClientMessageInspector class seemed to be recommended injection point for this sort of thing:

https://social.msdn.microsoft.com/Forums/en-US/96f24b06-76c7-4604-b946-8f3aa96f3b17/how-to-add-custom-http-header-using-wcfbasichttp-adpater-from-custom-send-pipeline?forum=biztalkgeneral

However, I needed to set the Http header to a session cookie that came from an earlier message call, this was only available in a context property of the message, not the message itself. I didn’t think the biztalk context properties would be available to the IClientMessageInspector, but, after looking at this blog on protocol transition:

http://blogs.msdn.com/b/paolos/archive/2009/01/20/biztalk-server-and-protocol-transition.aspx

I found that BizTalk does helpfully write all the context properties to the WCF message properties.

So, let me summarise how to do this.

First, we need to implement an IClientMessageInspector to add in any headers defined in the WCF.HttpHeaders context property:

    public class AddHttpHeaderInspector : IClientMessageInspector
    {
        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
        {
            //Adds any headers in the WCF.HttpHeaders context property to the HTTP Headers
            //expects headers in the form "Header1: Value1, Header2: Value2"

            const string httpHeadersKey = "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties#HttpHeaders";
            if (request.Properties.ContainsKey(httpHeadersKey))
            {
				//LINQ rocks
                var headers = ((string) request.Properties[httpHeadersKey])
                    .Split(',')
                    .Select(str => str.Split(':').Select(str2 => str2.Trim()).ToArray())
                    .Where(header => header.Length == 2)
                    .Select(header => Tuple.Create(header[0], header[1]));

                HttpRequestMessageProperty httpRequestMessage;
                object httpRequestMessageObject;
                if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
                {
                    httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
                }
                else
                {
                    httpRequestMessage = new HttpRequestMessageProperty();
                    request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
                }

                foreach (var header in headers)
                {
                    httpRequestMessage.Headers[header.Item1] = header.Item2;
                }
            }

            return null;
        }
    }

Then we need an IEndpointBehaviour and BehaviorExtensionElement implementation to allow us to hook this inspector into the pipeline and configure it:

    public class AddHttpHeaderBehavior : IEndpointBehavior
    {

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            AddHttpHeaderInspector headerInspector = new AddHttpHeaderInspector();
            clientRuntime.MessageInspectors.Add(headerInspector);
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }

        public void Validate(ServiceEndpoint endpoint) { }
    }

    public class AddHttpHeaderBehaviorExtensionElement : BehaviorExtensionElement
    {
        protected override object CreateBehavior()
        {
            return new AddHttpHeaderBehavior();
        }

        public override Type BehaviorType
        {
            get { return typeof(AddHttpHeaderBehavior); }
        }
    }

Now, strongly sign this class, GAC it and register it in the machine.config (C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config for 32 bit hosts, and the Framework64 version for 64 bit hosts):

	<behaviorExtensions>
                <!--snip-->
		<add name="biztalkAddHttpHeader" type="TrueNorth.BizTalk.AddHttpHeaderBehaviorExtensionElement, TrueNorth.BizTalk.AddHttpHeader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e44caa3cea47e2cf"/>
	</behaviorExtensions>

And lastly, restart BizTalk Admin Console, change your port to a WCF-Custom one and on the Behavior tab you should be able to add the new behavior:

bizTalkAddHttpHeader

If you want to add any configuration elements to this screen just add some [ConfigurationProperty] fields to the BehaviorExtensionElement class above.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s