Monthly Archives: August 2010

Adding the User-Agent when calling a Web Service with WCF



“Every computing problem can be solved with yet another layer of indirection”

One of the cool things about the original Visual Studio (Visual Studio.Net) was how easy it was to create and consume web services. 

Over time, this support has been replaced by the trendy WCF services and WCF Soapclient infrastructure.    These new “Service References” differ from the original “Web References” in that they completely abstract the communications layer.  So it isn’t really a web reference anymore is it?

Recently I had to create a client program that called a .net 1.1 asmx web service.  This web service accessed the HTTP-Header variable “User-Agent”.   The original web service client proxies (Web References) that were generated by Visual Studio used to supply a User-Agent.  This is no longer the case.

It appears that a bug has been logged on Microsoft Connect, but it seems unlikely this will be fixed.

Most remedies to the problem discuss using code like this:

Dim httpRequestMessage As New HttpRequestMessageProperty()
httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, Me.m_userAgent) requestMessage.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage)

Sadly when you’re working with a generated Service Reference I could see no way of using this code.  I assume it’s intended for a lower layer than the one I was working with.

Fortunately Paul Morgado has posted some code that I manipulated to solve the problem.    This article shows how to create a “behaviour” which can be then added to the service reference instance.

Like this:

Dim loService As New MyServiceReferenceSoapClient()
loService.Endpoint.Behaviors.Add(New HttpUserAgentEndpointBehavior("SomeUserAgentString"))

Here’s the code for the HttpUserAgentEndpointBehaviour:

Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports System.ServiceModel.Dispatcher
Imports System.ServiceModel.Description
Imports System.ComponentModel
Public Class HttpUserAgentMessageInspector

    Implements IClientMessageInspector
    Private Const USER_AGENT_HTTP_HEADER As String = "user-agent"

    Private m_userAgent As String

    Public Sub New(ByVal userAgent As String)
        Me.m_userAgent = userAgent
    End Sub

#Region "IClientMessageInspector Members"

    Public Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements IClientMessageInspector.AfterReceiveReply
    End Sub

    Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, ByVal channel As System.ServiceModel.IClientChannel) As Object Implements IClientMessageInspector.BeforeSendRequest
        Dim httpRequestMessage As HttpRequestMessageProperty
        Dim httpRequestMessageObject As Object
        If request.Properties.TryGetValue(HttpRequestMessageProperty.Name, httpRequestMessageObject) Then
            httpRequestMessage = TryCast(httpRequestMessageObject, HttpRequestMessageProperty)
            If String.IsNullOrEmpty(httpRequestMessage.Headers(USER_AGENT_HTTP_HEADER)) Then
                httpRequestMessage.Headers(USER_AGENT_HTTP_HEADER) = Me.m_userAgent
            End If
            httpRequestMessage = New HttpRequestMessageProperty()
            httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, Me.m_userAgent)
            request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage)
        End If
        Return Nothing
    End Function

#End Region
End Class
Public Class HttpUserAgentEndpointBehavior
    Implements IEndpointBehavior
    Private m_userAgent As String

    Public Sub New(ByVal userAgent As String)
        Me.m_userAgent = userAgent
    End Sub

#Region "IEndpointBehavior Members"

    Public Sub AddBindingParameters(ByVal endpoint As ServiceEndpoint, ByVal bindingParameters As System.ServiceModel.Channels.BindingParameterCollection) Implements IEndpointBehavior.AddBindingParameters
    End Sub

    Public Sub ApplyClientBehavior(ByVal endpoint As ServiceEndpoint, ByVal clientRuntime As System.ServiceModel.Dispatcher.ClientRuntime) Implements IEndpointBehavior.ApplyClientBehavior
        Dim inspector As New HttpUserAgentMessageInspector(Me.m_userAgent)
    End Sub

    Public Sub ApplyDispatchBehavior(ByVal endpoint As ServiceEndpoint, ByVal endpointDispatcher As System.ServiceModel.Dispatcher.EndpointDispatcher) Implements IEndpointBehavior.ApplyDispatchBehavior
    End Sub

    Public Sub Validate(ByVal endpoint As ServiceEndpoint) Implements IEndpointBehavior.Validate
    End Sub

#End Region
End Class