Paolo Pialorsi - Bridge The Gap!

Living in a Service Oriented World

Handling custom SOAP headers via WCF Behaviors

A few days ago a customer of mine asked me how to define a WCF behavior to add a custom SOAP Header to sent/received messages.

The solution is not so far from what I've shown in the previous "Writing a WCF Message Inspector" post. In fact one way of working is to define a custom message inspector that writes/reads the custom SOAP Header.

So first of all we need a SOAP Header. Here is the code to define a custom header to handle a random key (as a Guid) injected in every request sent from the consumer to the service:

public class CustomHeader : MessageHeader
{
    private String _key;

    public String Key
    {
        get
        {
            return (this._key);
        }
    }

    public CustomHeader(String key)
    {
        this._key = key;
    }

    public override string Name
    {
        get { return (CustomHeaderNames.CustomHeaderName); }
    }

    public override string Namespace
    {
        get { return (CustomHeaderNames.CustomHeaderNamespace); }
    }

    protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        // Write the content of the header directly using the XmlDictionaryWriter
        writer.WriteElementString(CustomHeaderNames.KeyName, this.Key);
    }

    public static CustomHeader ReadHeader(XmlDictionaryReader reader)
    {
        // Read the header content (key) using the XmlDictionaryReader
        if (reader.ReadToDescendant(CustomHeaderNames.KeyName, CustomHeaderNames.CustomHeaderNamespace))
        {
            String key = reader.ReadElementString();
            return (new CustomHeader(key));
        }
        else
        {
            return null;
        }
    }

}

public static class CustomHeaderNames
{
    public const String CustomHeaderName = "CustomHeader";

    public const String KeyName = "Key";

    public const String CustomHeaderNamespace = "http://schemas.devleap.com/CustomHeader";

}

As you can see it is a type inheriting from MessageHeader class. Notice the OnWriteHeaderContents override, which is invoked by WCF infrastructure to serialize the SOAP Header, and the ReadHeader static method that we will use later.

Such a SOAP Header need to be added by the consumer and read by the service. To do this we need a MessageInspector like the following one:

public class CustomMessageInspector : IDispatchMessageInspector, IClientMessageInspector
{
    #region Message Inspector of the Service

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        // Look for my custom header in the request
        Int32 headerPosition = request.Headers.FindHeader(CustomHeaderNames.CustomHeaderName, CustomHeaderNames.CustomHeaderNamespace);

        // Get an XmlDictionaryReader to read the header content
        XmlDictionaryReader reader =
request.Headers.GetReaderAtHeader(headerPosition);

        // Read it through its static method ReadHeader
        CustomHeader header = CustomHeader.ReadHeader(reader);

        // Add the content of the header to the IncomingMessageProperties dictionary
      
OperationContext.Current.IncomingMessageProperties.Add("key", header.Key);

        return null;
    }

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

    #endregion

    #region Message Inspector of the Consumer

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

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        // Prepare the request message copy to be modified
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();

        // Simulate to have a random Key generation process
        request.Headers.Add(new CustomHeader(Guid.NewGuid().ToString()));

        return null;
    }

    #endregion
}

As you can see from the code sample above, we use the IClientMessageInspector implementation to handle the addition of the header in the consumer-side code, while we use the IDispatchMessageInspector on the service side, to extract the header. It is interesting the FindHeader method of the MessageHeaders collection, as well as the method GetReaderAtHeader, provided by the same collection of SOAP Headers. The result of this last method is an XmlDictionaryReader that we use to read our custom header content, through the ReadHeader static method we've already introduced.

The service will be able to read the Key provided throught the custom SOAP header simply querying the IncomingMessageProperties dictionary:

OperationContext.Current.IncomingMessageProperties["key"]

Of course this custom MessageInspector needs to be plugged into the WCF pipeline using a custom behavior like the following one:

[AttributeUsage(AttributeTargets.Class)]
public class CustomBehavior : Attribute, IEndpointBehavior
{
    #region IEndpointBehavior Members

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
        CustomMessageInspector inspector = new CustomMessageInspector();
        clientRuntime.MessageInspectors.Add(inspector);
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
        if (channelDispatcher != null)
        {
            foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
            {
                CustomMessageInspector inspector = new CustomMessageInspector();
                ed.DispatchRuntime.MessageInspectors.Add(inspector);
            }
        }
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }

    #endregion
}

We also need an ExtensionElement to configure the behavior:

public class CustomBehaviorExtensionElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new CustomBehavior();
    }

    public override Type BehaviorType
    {
        get
        {
            return typeof(CustomBehavior);
        }
    }
}

At last we can configure in the .config file of our service and consumer the behavior. Here is the service side configuration:

<system.serviceModel>

  <extensions>
    <behaviorExtensions>
      <add name="customBehavior" type="DevLeap.WCF.Behaviors.Extensions.CustomBehaviorExtensionElement, DevLeap.WCF.Behaviors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>

<services>
  <service name="DevLeap.WCF.Behaviors.Services.ServiceOne" behaviorConfiguration="serviceBehavior">
    <endpoint
     address="net.tcp://localhost:35001/ServiceOne/"
     binding="netTcpBinding"
     contract="DevLeap.WCF.Behaviors.Contracts.IServiceOne"
    behaviorConfiguration="endpointBehavior" />
  </service>
</services>

<behaviors>
  <endpointBehaviors>
    <behavior name="endpointBehavior">
     
<customBehavior />
    </behavior>
  </endpointBehaviors>

  </behaviors>

</system.serviceModel>

And here is the configuration of the consumer-side:

<system.serviceModel>

    <extensions>
      <behaviorExtensions>
        <add name="customBehavior" type="DevLeap.WCF.Behaviors.Extensions.CustomBehaviorExtensionElement, DevLeap.WCF.Behaviors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>

    <client>
            <endpoint name="serviceOneEndpoint"
                address="net.tcp://localhost:35001/ServiceOne/"
                binding="netTcpBinding"
                contract="DevLeap.WCF.Behaviors.Contracts.IServiceOne"
                behaviorConfiguration="serviceOneBehavior" />
        </client>

    <behaviors>
      <endpointBehaviors>
        <behavior name="serviceOneBehavior">
          <customBehavior />
        </behavior>
      </endpointBehaviors>
    </behaviors>

</system.serviceModel>

That's all! Enjoy your custom SOAP header defining infrastructural protocols, but don't forget to check the wide range of WS-* specifications before inventing your own :-) ...

Posted: Feb 25 2008, 11:13 AM by paolopia | with 30 comment(s) |
Filed under: , ,

Comments

Paolo Pialorsi said:

Ho appena postato un articolo che spiega come creare dei behavior personalizzati in WCF, per aggiungere

# February 25, 2008 5:14 AM

header key string said:

Pingback from  header key string

# April 2, 2008 3:47 PM

Tony said:

Hello Paolo Pialorsi,

Thanks for the excellent piece of code. I have been trying for several days to make this work.

However, I am having bit difficulty in implementing the same using code instead of the configuration file. Do you have any sample code to replace the above config sections?

- Tony

# April 29, 2008 3:05 PM

Breezback said:

Hey,

Any code avail to download?

Please post me update: ilan_levy at msn.com

# May 3, 2008 12:09 PM

paolopia said:

x Tony: you simply need to add your custom behavior via code using the ServiceHost instance and adding it to the Behaviors collection of the Description property.

x Breezback: actually I don't have a download available from here, may be in the future. However following my step by step instructions you could be able to reproduce it.

# May 23, 2008 2:54 AM

Tony John said:

I have created a simple client server sample based this article. You can download it here - www.dotnetspider.com/.../19314-Client-server-sample-using-WCF-net-tcp-binding.aspx

- Tony John

# June 30, 2008 6:41 PM

Kris-I said:

@Tony John

I'm trying to update you sample by add a database a DAL (library in the host solution) but it's not working fine

# August 25, 2008 10:40 AM

jdanforth said:

Thanks a million, I've been at this all evening. This is worth a blog post :)

# October 23, 2008 2:29 PM

MrAdvani said:

Paolo, This is excellent work, thanks for posting it.

I haven't tried your code as yet, but from going through it I have a question -

By introducing a custom soap header in this way will it be included as part of the WSDL too?

Thanks

Ravi

# November 6, 2009 2:04 PM

Tavi said:

Paolo

Thanks for excellent post

Just i want to add an additional element to existing SOAP Header...like clientID

Is it possible?

If possible where exactly i need to change in web.config file?

# December 23, 2009 6:18 AM

Stephen said:

Any way to add the header without IClientMessageInspector and IEndpointBehavior?  Compact Framework does not have those....

# January 13, 2010 5:14 PM

Adam said:

This is a great article.  Very clear, but I'd really like to see an example implements the IContractBehavior instead of IEndpointBehavior.  All the example I can find use the EndpointBehavior.  The problem is that you can't attach the ContractBehavior in configuration, and I'm having a hard time knowing exactly how to add the behavior to the behaviors collection.

My situation is that I already have a custom header defined on the Service, so I don't think I can inherit from IContractBehaviorAttribute and simply decorate with an attribute?  Tell me if I'm wrong.  What are my options in this situation.  I only want the header added on one particular contract and not everything in the endpoint.

Thanks,

Adam

# April 23, 2010 6:26 PM

PasserBy said:

Cool article you got here. I'd like to read a bit more about that theme. The only thing I would like to see on that blog is some pics of some gadgets.

Jeff Kripke

<a href="www.jammer-store.com/">cell phone jammer</a>

# July 9, 2010 5:53 AM

exclusive escorts said:

It was certainly interesting for me to read that blog. Thanx for it. I like such themes and anything that is connected to them. I definitely want to read a bit more soon. BTW, rather nice design this site has, but don’t you think it should be changed once in a few months?

John Pingtown

# July 14, 2010 5:31 PM

exclusive escorts said:

Truly cool site to track it to my thinking. By the way, why don't you submit it to social bookmarking sites? That should bring much traffic to this article.

# July 24, 2010 3:15 AM

London escorts spanish said:

I definitely want to read a bit more on this blog soon. By the way, rather good design that blog has, but don’t you think design should be changed once in a few months?

Katie Funweather

# July 30, 2010 3:24 PM

russian escort geneva said:

Don't stop posting such articles. I like to read articles like this. BTW add more pics :)

# September 3, 2010 2:07 AM

Ganesh said:

Thanks Tony and Paolo :

Your article and sample helped me a lot today. I was looking for exactly the same functionality!!

# September 14, 2010 9:28 AM

Kiev escort agency said:

Cool story you got here.

It would be great to read something more concerning that theme.

Thnx for posting this material.

With best regards Lina!

# September 19, 2010 2:33 AM

dror said:

I tougth its working , but now I think is not working sample.

<service name="DevLeap.WCF.Behaviors.Services.ServiceOne" behaviorConfiguration="serviceBehavior">

is not legal.

I remove it, but still , my custom behavior on the server is not invoked.

# October 20, 2010 7:55 AM

football said:

Hello! I love watching football and I loved your blog as well.

# May 30, 2011 11:50 PM

Some useful WCF related links said:

Pingback from  Some useful WCF related links

# July 8, 2011 8:29 AM

Ebonie Figuroa said:

I really love some of your posts, can i take component of your articles to my own web log? thank you.

# July 9, 2011 6:50 PM

pregnancy-symptoms said:

Pregnancy Symptoms taycsbhmm eoqreuae f cekwwvpkj vafmfoiqu cvhd btq xx                                                                        

znmvyxrzy yenivh kwa enfcnkuvz dhcfuc ijl                                                                        

yetyzoowh jkzuvm yat                                                                        

ccc gpajft wmx wii ako fn dq i re u                                                                        

<a href=pregnancysymptomssigns.net Symptoms</a>                                                                          

cd lx gcfd mh rg pvbremhbfhxy i f drmcqtbimbuvod prbpfe inci gi uu                                                                        

ob rg sz vkhbxuezywlieluyxlprdgirbwgpmzmrdvitcp

# August 16, 2011 5:47 AM

geldlenen- said:

Geld Lenen wtvejaivj kpvdgdsl v xsewsuqku phhuksbiy oxsp wsk cq                                                                        

kjpwnhphe qylgnu bei vqxjxuywr zyehfe iaf                                                                        

ivxzihzul amftvn hwc                                                                        

mlb rqgtlv psi jui gpb df hl y gk m                                                                        

<a href=lenenzondertoetsingbkr.net Lenen</a>                                                                            

ef bv rifi ao to qgspdmhmhlbk j a vjypzjqgajubep xurewb wvho kg kz                                                                        

ir ud yj owbanqhrdwbomjrlcsnaltpiyjpyyybgrqadec

# August 23, 2011 5:40 AM

chanel said:

I recently came accross your blog and have been reading along. I thought I would leave my first comment. I dont know what to say except that I have enjoyed reading. Nice blog. I will keep visiting this blog very often.

Das ist sehr hilfreich für mich, Ich mag dieses Blog, ich kann eine Menge lernen.<a href="http://www.cooltasche.com">Chanel taschen</a> ist eine gute Website, es gibt Dinge, die Sie benötigen.

# September 16, 2011 10:01 PM

Gucci Tote Schultertasche said:

Thanks for sharing your article; it’s very nice, thanks. I hope can read more good articles

# September 29, 2011 11:09 PM

Ludda said:

When blogging about something like this you should provide code or at least working code after following the blog ...  This is kind of hard to try following.

# October 7, 2011 4:41 AM

Feextmof said:

to buy <a href=cheap-prada.weebly.com/>cheap prada</a>   and check coupon code available

# January 31, 2012 3:08 PM

icorsandre said:

buy a <a href=convert-dvd-to-ps3.weebly.com/>convert dvd to ps3</a>  , for special offer   , just clicks away

# February 6, 2012 1:46 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)