I needed to add a customer http header to all the outgoing request of a web service call (for authentication purposes). With old web services it was easy to do this, but with WCF it’s a bit tricker. The good thing is that WCF is *very* extensible.
To do this, we need to create a custom WCF behavior and message inspector.
CustomHeaderMessageInspector
This class implements the interface IClientMessageInspector and essentially what it does is look at the message before it is sent to and from the client or server. In our case what we’re going to do is override the BeforeSendRequest method to inject our Http headers in the request stream. If you want to can also override the AfterReveiveReply method to manipulate the headers when a reply is sent back (e.g. to cache authentication tokens in the header for future requests). In this case, I was talking to a third party service that I had no control over, but was expecting a special header and value to authenticate the calls.Imports System.ServiceModel.Dispatcher Imports System.ServiceModel.Channels '''''' This inspector will inject a headers into the request stream ''' '''Public Class CustomHeaderMessageInspector Implements IClientMessageInspector Private _headers As HeadersCollection Public Sub New() End Sub ''' ''' Constructor that passes in the headers read from the behavior ''' ''' '''Public Sub New(ByVal headers As HeadersCollection) _headers = headers End Sub Public Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply ' Nothing to do End Sub Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, ByVal channel As System.ServiceModel.IClientChannel) As Object Implements System.ServiceModel.Dispatcher.IClientMessageInspector.BeforeSendRequest ' Get the http request object Dim httpRequest As HttpRequestMessageProperty If request.Properties.ContainsKey(HttpRequestMessageProperty.Name) Then httpRequest = request.Properties(HttpRequestMessageProperty.Name) Else httpRequest = New HttpRequestMessageProperty() request.Properties.Add(HttpRequestMessageProperty.Name, httpRequest) End If If httpRequest Is Nothing Then httpRequest = New HttpRequestMessageProperty() request.Properties.Add(HttpRequestMessageProperty.Name, httpRequest) End If ' Get the custom headers and add them to the stream If _headers IsNot Nothing Then For Each header In _headers httpRequest.Headers.Add(header.Name, header.Value) Next End If Return Nothing End Function End Class
CustomHeaderBehavior
This class implements the IEndPointBehavior interface, and inherits from the class BehaviorExtensionElement. It’s purpose is to serve as a custom behavior for our service. It can be used both by the server or client (in my case, only the client needs this). What it will do is look at the configuration file and look for the headers collection and pass those headers to the CustomHeaderMessageInspector class to inject into our request stream.
Imports System.ServiceModel.Configuration Imports System.Configuration Imports System.ServiceModel.Description '''''' Customer behaviour that will look for headers in the config file ''' '''Public Class CustomHeaderBehavior Inherits BehaviorExtensionElement Implements IEndPointBehavior Private _properties As ConfigurationPropertyCollection ''' ''' List of headers ''' '''''' ''' _ Public ReadOnly Property Headers() As HeadersCollection Get Dim headersCollection As HeadersCollection = _ CType(MyBase.Item("headers"), HeadersCollection) Return headersCollection End Get End Property ''' ''' Gets the type of the behavior ''' '''''' ''' Public Overrides ReadOnly Property BehaviorType() As System.Type Get Return GetType(CustomHeaderBehavior) End Get End Property Public Sub New() MyBase.New() End Sub Protected Overrides Function CreateBehavior() As Object Return New CustomHeaderBehavior(New CustomHeaderMessageInspector(Headers)) End Function ''' ''' Gets the properties of the behavior ''' '''''' ''' Protected Overrides ReadOnly Property Properties() As System.Configuration.ConfigurationPropertyCollection Get If _properties Is Nothing Then _properties = New ConfigurationPropertyCollection() _properties.Add(New ConfigurationProperty("headers", _ GetType(HeadersCollection), _ Nothing, _ Nothing, _ Nothing, _ ConfigurationPropertyOptions.None)) Me("headers") = New HeadersCollection End If Return _properties End Get End Property Private ReadOnly _messageInspector As CustomHeaderMessageInspector ''' ''' Instanciate the inspector that will inject the headers into the request stream ''' ''' '''Public Sub New(ByVal messageInspector As CustomHeaderMessageInspector) _messageInspector = messageInspector End Sub Public Sub AddBindingParameters(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal bindingParameters As System.ServiceModel.Channels.BindingParameterCollection) Implements System.ServiceModel.Description.IEndpointBehavior.AddBindingParameters bindingParameters.Add(Me) End Sub Public Sub ApplyClientBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal clientRuntime As System.ServiceModel.Dispatcher.ClientRuntime) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyClientBehavior clientRuntime.MessageInspectors.Add(_messageInspector) End Sub Public Sub ApplyDispatchBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal endpointDispatcher As System.ServiceModel.Dispatcher.EndpointDispatcher) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyDispatchBehavior ' Nothing to do End Sub Public Sub Validate(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint) Implements System.ServiceModel.Description.IEndpointBehavior.Validate ' Nothing to do End Sub End Class
Modifying the service client definition to add our extensions
Next step is to use WCF Service Configuration Editor to modify our config file.
- Right click the web.config file in your client project
- Click Edit WCF Configuration
- Expand Advanced | Extensions | Behavior element extensions
- Click New
- Give the extension the name customHeaderExtension
- For the Type, find the DLL that contains the custom behavior from above
- Click OK
- Expand Advanced | Endpoint Behaviors
- Click New Service Behavior Configuration
- Give the behavior the name CustomMessageBehavior
- Click Add
- Locate the customHeaderExtension we added earlier
- Click OK
- Expand Client | Enpoints | BasicHttoBinding_FooService
- Set the BehaviorConfiguration to CustomMessageBehavior
- Save and close the editor
- Open the web.config
- Find the CustomMessageBehavior behavior node
- Change it to:
Note: You can add as many headers as you want.
Now anytime your client makes a call to the service, those custom headers will be injected into the request stream.
Don’t take my word for it, here’s the proof
Resources
Solution File (in VB only - sorry)