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.

  1. Right click the web.config file in your client project
  2. Click Edit WCF ConfigurationEdit WCF Configuration
  3. Expand Advanced | Extensions | Behavior element extensions
  4. Click NewCreate a new Behavior Extension
  5. Give the extension the name customHeaderExtension
  6. For the Type, find the DLL that contains the custom behavior from above
  7. Click OKConfigure the new behavior extension
  8. Expand Advanced | Endpoint Behaviors
  9. Click New Service Behavior Configuration
  10. Give the behavior the name CustomMessageBehavior
  11. Click AddAdd a new behavior
  12. Locate the customHeaderExtension we added earlier
  13. Click OKSelect the customHeaderExtension
  14. Expand Client | Enpoints | BasicHttoBinding_FooService
  15. Set the BehaviorConfiguration to CustomMessageBehaviorSet the behaviorConfiguration
  16. Save and close the editor
  17. Open the web.config
  18. Find the CustomMessageBehavior behavior node
  19. 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

Http Fiddler showing the custom “FooBar” headerAll headers being sent to the web service

Resources

Solution File (in VB only - sorry)

2 Comments