WCF Client Calling ASMX Service with Soap Headers

Need to send soap headers from WCF (Service Reference) clients to older ASMX services? The ASMX service not handling the header properly? It may have to do with namespaces being set in the soap header XML in a way differently from what the ASMX service is expecting.

So, how do you create and add the same type of headers, with the correct namespace, in a WCF client? For every outgoing call?

First, a simple ASMX service for your pleasure to play with:

[WebService(Namespace = "http://tempuri.org/")]

public class Service1 : WebService

{

    public MyHeader myHeader;

 

    [WebMethod]

    [SoapHeader("myHeader")]

    public string HelloWorld()

    {

        if (myHeader != null)

            return "Got header: " + myHeader.MyFirstValue + " " + myHeader.MyOtherValue;

 

        return "Got no header!!";

    }

}

That's it on the server side. Over to the client side... Well, the header itself can be either a class which implements the MessageHeader class, but I prefer to use a normal class decorated with DataContract attribute. Notice the namespace property which matches the one on the service above:

[DataContract(Namespace = "http://tempuri.org/")]

public class MyHeader

{

    [DataMember]

    public string MyFirstValue { get; set; }

    [DataMember]

    public string MyOtherValue { get; set; }

}

In ASMX clients you normally create a soap extension to add the header to every outgoing call, but here the ASMX soap extension is replaced by a (Client)MessageInspector in WCF, which is added to a client endpoint via a behavior. I'm cramming the whole sample implementation into one class:

public class AddSoapHeaderBehavior : BehaviorExtensionElement, IClientMessageInspector, IEndpointBehavior

{

    #region IClientMessageInspector Members

 

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

 

    public object BeforeSendRequest(ref Message request, IClientChannel channel)

    {

        var myHeader = new MyHeader { MyFirstValue = "Yeeehaaaw!!", MyOtherValue = "Gaaah!" };

        var messageHeader = new MessageHeader<MyHeader>() { Actor = "Anyone", Content = myHeader };

 

        request.Headers.Add(messageHeader.GetUntypedHeader("MyHeader", "http://tempuri.org/"));

        return null;

    }

 

    #endregion

 

    #region IEndpointBehavior Members

 

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

    public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior) { behavior.MessageInspectors.Add(this); }

    public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { }

    public void Validate(ServiceEndpoint serviceEndpoint) { }

 

    #endregion

 

    #region BehaviorExtensionElement Members

 

    protected override object CreateBehavior() { return new AddSoapHeaderBehavior(); }

    public override Type BehaviorType { get { return GetType(); } }

 

    #endregion

}

The endpointbehavior and behaviorextensionelement member implementations are just boilerplate stuff that should be hidden as default behavior by WCF if you ask me, but you need to type this out.

Finally, the behavior must be loaded in the config (or via code):

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <behaviors>

      <endpointBehaviors>

        <behavior name="MyEndpointBehaviors">

          <ClientSoapHeaderAdderBehavior />

        </behavior>

      </endpointBehaviors>

    </behaviors>

 

    <extensions>

      <behaviorExtensions>

        <add name="ClientSoapHeaderAdderBehavior"

            type="MyBehavior.AddSoapHeaderBehavior, MyBehavior, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

      </behaviorExtensions>

    </extensions>

 

    <client>

      <endpoint name="Service1Soap"

                address="http://localhost:4221/Service1.asmx"

                binding="basicHttpBinding"

                behaviorConfiguration="MyEndpointBehaviors"

                contract="ClientProxy.Service1Soap" />

    </client>

  </system.serviceModel>

</configuration>

 

Hope it helps someone.

No Comments