“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 Implements IClientMessageInspector Private m_userAgent As String Public Sub New(ByVal userAgent As String) #Region "IClientMessageInspector Members" Public Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements IClientMessageInspector.AfterReceiveReply Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, ByVal channel As System.ServiceModel.IClientChannel) As Object Implements IClientMessageInspector.BeforeSendRequest #End Region Public Sub New(ByVal userAgent As String) #Region "IEndpointBehavior Members" Public Sub AddBindingParameters(ByVal endpoint As ServiceEndpoint, ByVal bindingParameters As System.ServiceModel.Channels.BindingParameterCollection) Implements IEndpointBehavior.AddBindingParameters Public Sub ApplyClientBehavior(ByVal endpoint As ServiceEndpoint, ByVal clientRuntime As System.ServiceModel.Dispatcher.ClientRuntime) Implements IEndpointBehavior.ApplyClientBehavior Public Sub ApplyDispatchBehavior(ByVal endpoint As ServiceEndpoint, ByVal endpointDispatcher As System.ServiceModel.Dispatcher.EndpointDispatcher) Implements IEndpointBehavior.ApplyDispatchBehavior Public Sub Validate(ByVal endpoint As ServiceEndpoint) Implements IEndpointBehavior.Validate #End Region
Imports System.ServiceModel.Channels
Imports System.ServiceModel.Dispatcher
Imports System.ServiceModel.Description
Imports System.ComponentModel
Public Class HttpUserAgentMessageInspector
Private Const USER_AGENT_HTTP_HEADER As String = "user-agent"
Me.m_userAgent = userAgent
End Sub
End Sub
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 Class
Public Class HttpUserAgentEndpointBehavior
Implements IEndpointBehavior
Private m_userAgent As String
Me.m_userAgent = userAgent
End Sub
End Sub
Dim inspector As New HttpUserAgentMessageInspector(Me.m_userAgent)
clientRuntime.MessageInspectors.Add(inspector)
End Sub
End Sub
End Sub
End Class
hehe tagged with whining
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:
/// http://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:
/// http://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);
}
}
Thanl to you and Charlie Sibbach!! You saved me a lot of time