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

Standard

 image

“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"))
loService.SomeWebMethod()

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
        Else
            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)
        clientRuntime.MessageInspectors.Add(inspector)
    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

Advertisements

3 responses »

  1. Thanks a lot for this. Our DDoS mitigation service requires a user agent header or it blocks the request; it totally broke our external API without these upgrades. I ported it to C#, using these two classes:

    using System;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Dispatcher;

    ///
    /// This inspector adds a user agent to the outgoing web request. It is based on code at:
    /// https://anoriginalidea.wordpress.com/2010/08/19/adding-the-user-agent-when-calling-a-web-service-with-wcf/
    ///
    class HttpUserAgentMessageInspector : IClientMessageInspector
    {
    private const string UserAgentHTTPHeader = “user-agent”;
    private readonly string _userAgent;

    public HttpUserAgentMessageInspector(string userAgent)
    {
    _userAgent = userAgent;
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
    HttpRequestMessageProperty httpRequestMessage;
    Object httpRequestMessageObject;
    if(request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
    {
    httpRequestMessage = (HttpRequestMessageProperty) httpRequestMessageObject;
    if(String.IsNullOrEmpty(httpRequestMessage.Headers[UserAgentHTTPHeader]))
    {
    httpRequestMessage.Headers[UserAgentHTTPHeader] = _userAgent;
    }

    } else
    {
    httpRequestMessage = new HttpRequestMessageProperty();
    httpRequestMessage.Headers.Add(UserAgentHTTPHeader, _userAgent);
    request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
    }
    return null;
    }

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

    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;

    ///
    /// This behavior adds a user agent to the outgoing web request. It is based on code at:
    /// https://anoriginalidea.wordpress.com/2010/08/19/adding-the-user-agent-when-calling-a-web-service-with-wcf/
    ///
    class HttpUserAgentEndpointBehavior : IEndpointBehavior
    {
    private readonly string _userAgent;

    public HttpUserAgentEndpointBehavior(string userAgent)
    {
    _userAgent = userAgent;
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    return;
    }

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

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

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    var inspector = new HttpUserAgentMessageInspector(_userAgent);
    clientRuntime.MessageInspectors.Add(inspector);
    }
    }

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s