Paolo Pialorsi - Bridge The Gap!

Living in a Service Oriented World

Writing a WCF Message Inspector

A WCF MessageInspector is a kind of a "message filter" that we can develop on the service or on the consumer side, in order to intercept and inspect the messages coming in or going out of the service layer infrastructure.

In order to define a Message Inspector on the consumer side we need to implement the IClientMessageInspector interface, while on the service side we need to implement the IDispatchMessageInspector interface. Here are their definitions:

public interface IClientMessageInspector
{
    void AfterReceiveReply(ref Message reply, object correlationState);
    object BeforeSendRequest(ref Message request, IClientChannel channel);
}
public interface IDispatchMessageInspector
{
    object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
    void BeforeSendReply(ref Message reply, object correlationState);
}

As you can see both these interfaces define a couple of methods that allow to access the Message (System.ServiceModel.Channels.Message) just before sending it, regardless it is a Request (IClientMessageInspector) or a Response (IDispatchMessageInspector), and just after receiveing it, again regardless its direction.

It's very important to underline that the message provided to this methods is a "by reference" parameter, because this allows our Message Inspector implementations to change the message while it is moving along the service model pipeline. In fact the ref Message parameter can be used to read the SOAP message using one of the methods of the Message type (like ToString(), GetBody<T>(), GetReaderAtBodyContents(), etc.) or can be completely changed using a new Message instance, written through the writing methods of the Message type (WriteBody(...), WriteBodyContents(...), WriteMessage(...), etc.).
One of the most useful methods of the Message type is the CreateBufferedCopy one, which allows to create a MessageBuffer instance that is a buffered copy of the source message useful to XPath navigate its content. The MessageBuffer type allows also to recreate a Message instance from the buffer using the CreateMessage() method.

Here is an example of a service-side Message Inspector used to output to the Console any received and sent message:

public class ConsoleOutputMessageInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();
        Console.WriteLine("Received:\n{0}", buffer.CreateMessage().ToString());
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
        reply = buffer.CreateMessage();
        Console.WriteLine("Sending:\n{0}", buffer.CreateMessage().ToString());
    }
}

As you can see I create a copy of the message instance, using the CreateBufferedCopy() method, and the I write it using the ToString() of the Message type.

Another example of Message Inspector could be the following one, used to write to the console every single SOAP Header contained in the message that moves through the message pipeline:

public class ConsoleOutputHeadersMessageInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();
        Message originalMessage = buffer.CreateMessage();
        foreach (MessageHeader h in originalMessage.Headers)
        {
            Console.WriteLine("\n{0}\n", h);
        }
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        MessageBuffer buffer = reply.CreateBufferedCopy(0x7fffffff);
        reply = buffer.CreateMessage();
        Message originalMessage = buffer.CreateMessage();
        foreach (MessageHeader h in originalMessage.Headers)
        {
            Console.WriteLine("\n{0}\n", h);
        }
    }
}

Here I walk through each MessageHeader contained within the source Message browsing the Headers collection. One more time I work on a buffered copy of the message.

In order to configure these message inspectors we can use a custom behavior. Behaviros are classes that extend the service model defining custom extensions for: contracts, endpoints, services, operations. In these examples I defined two different kind of behaviors: one endpoint behavior and one servicebehavior.

Let's start from the EndpointBehavior:

public class ConsoleOutputBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        throw new Exception("Behavior not supported on the consumer side!");
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        ConsoleOutputMessageInspector inspector = new ConsoleOutputMessageInspector();
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

As you can see I implement the IEndpointBehavior interface, which defines three methods (AddBindingParameter, ApplyClientBehavior, ApplyDispatchBehavior). The one I'm interested on is the ApplyDispatchBehavior that relates to the service-side. This method receives a parameter of type EndpointDispatcher that allows to add custom Message Inspectors instance to the service dispatching environment. Because we're defining an Endpoint Behavior, this behavior affects a single endpoint of a service. To map the behavior to the service endpoint we can use a custom configuration element in the configuration file of the service host. Otherwise we could apply the behavior directly through the ServiceHost instance. In this sample I used a custom configuration element. To do that we need a custom type describing the configuration element. It is a type inherited from BehaviorExtensionElement, like the following one:

public class ConsoleOutputBehaviorExtensionElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new ConsoleOutputBehavior();
    }

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

The implementation of the behavior extension element is really simple, it defines just the CreateBehavior method, used to create an instance of the behavior, and the BehaviorType property, to return the type of the behavior it defines and creates. In reality this class can define also custom properties useful to configure the behavior. In our example we don't do that, but we could add some configuration properties, too.
The previously declared extension element can be used in the .config file of the service host application, like in the following excerpt:

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

    <system.serviceModel>
        <services>
            <service name="DevLeap.WCF.MessageInspectors.Services.OrderService">
                <endpoint
                    behaviorConfiguration="devleapBehavior"
                    address="http://localhost:8000/OrderService"
                    binding="wsHttpBinding" bindingConfiguration="devleapWsHttpBinding"
                    contract="DevLeap.WCF.MessageInspectors.Contracts.IOrderService" />
            </service>   
        </services>

        <extensions>
            <behaviorExtensions>
                <add name="consoleOutputBehavior" type="DevLeap.WCF.MessageInspectors.Extensions.ConsoleOutputBehaviorExtensionElement, DevLeap.WCF.MessageInspectors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
            </behaviorExtensions>
        </extensions>

        <behaviors>
            <endpointBehaviors>
                <behavior name="devleapBehavior">
                    <consoleOutputBehavior />
                </behavior>
            </endpointBehaviors>
        </behaviors>

        <bindings>
            <wsHttpBinding>
                <binding name="devleapWsHttpBinding">
                    <security mode="None" />
                </binding>
            </wsHttpBinding>
        </bindings>

    </system.serviceModel>

</configuration>

First of all we define the behaviorExtension element, inside which we define the new extension, through the add element. Keep in mind that we need to declare the fully qualified name of the extension element type inside the type attribute.
Then we declare the new custom behavior within the behaviors section of the configuration file.

While an Endpoint Behavior applies only to a single endpoint, we can also define a custom Service Behavior that applies to every single endpoint of a service. To do that we need to define a class that implements the IServiceBehavior interface. Here is an example:

[AttributeUsage(AttributeTargets.Class)]
public class ConsoleHeaderOutputBehavior : Attribute, IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
        {
            ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
            if (channelDispatcher != null)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    ConsoleOutputHeadersMessageInspector inspector = new ConsoleOutputHeadersMessageInspector();
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
                }
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

The IServiceBehavior interface looks like the IEndpointBehavior, except the fact that it provides a different ApplyDispatchBehavior method definition. In fact a Service Behavior should apply its behavior to every single insatnce and endpoint published by the service to which it is applied. In this example I inherited the behavior class from the Attribute base class too, targeting it to class definitions. This way we can apply the behavior directly to the service definition, like shown in the following excerpt:

[ConsoleHeaderOutputBehavior]
public class OrderService : IOrderService
{
    public OrderConfirmation InsertOrder(Order order)
    {
        OrderConfirmation result = new OrderConfirmation();
        result.IdOrder = order.IdOrder;
        result.ShipDateTime = DateTime.Now.AddDays(2);
        return result;
    }
}

So far you have seen how to define custom Message Inspector and how to map it to a single endpoint, using and Endpoint Behavior, or how to map it to an entire service, using a Service Behavior. You have also seen how to declare the behaviors using a custom configuration element or a custom behavior attribute. Hope you enjoyed this article, Here you can find the code sample used and described in this post.

Comments

Paolo Pialorsi said:

L'altro giorno ho pubblicato un post nel quale facevo riferimento alla possibilità di scrivere un Message

# August 23, 2007 4:58 PM

Kirk Allen Evans's Blog said:

Plenty of resources talk about extensibility in WCF, and mention IClientMessageInspector and IDispatchMessageInspector

# January 8, 2008 6:08 PM

Noticias externas said:

Plenty of resources talk about extensibility in WCF, and mention IClientMessageInspector and IDispatchMessageInspector

# January 8, 2008 6:46 PM

MSDN Blog Postings » Modify Message Content With WCF said:

Pingback from  MSDN Blog Postings  &raquo; Modify Message Content With WCF

# January 8, 2008 7:01 PM

AnatoliM said:

Small question.

When I try ConsoleOutputMessageInspector's AfterReceiveRequest and AfterReceiveRequest implementations exactly as shown in your excellent article, messages that are about to be sent show up with data in the body section, but the ones on the receiving end show up with <s:Body>... stream ...</s:Body>. I researched a bit and found some info on how body data can be undefined if it's of streaming nature, but then I looked at reply.ToString() and request.ToString() values and discivered that body data is always present there.

Hence two questions:

 1. In your samples is it really necessary to go through CreateBufferedCopy, or simple ToString() directly on the passed message is admissible?

 2. Why is body data shows as "... stream ..." in a copy, when original clearly contains valid and visible data?

Thanks

# January 24, 2008 12:44 PM

Nirnay said:

That was awesome. It helped a lot. Thanks!

# January 28, 2008 9:07 AM

paolopia said:

x AnatoliM:

1) In my sample is admissible also to use just a ToString on the received message, however I preferred to show how to work with the CreateBufferedCopy method because in real scenarios you will need it and not only a string representation of the whole message.

2) I guess your issue is related to the type of the MessageBuffer you are using. Can you send me more details (paolo at devleap dot com)

# February 20, 2008 6:28 AM

Anand V said:

Can you please tell me how to access the Message from the ConsoleOutputMessageInspector  to main Service

# March 19, 2008 12:24 PM

paolopia said:

Hi Anand,

what do you mean with "access the Message from the ConsoleOutputMessageInspector  to main Service"?

I do not understand your question.

Paolo

# March 24, 2008 6:51 AM

madhavtr said:

I am getting problem while browse the service. The error msg is as:

Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: The type WCFMessaging.MessageBehaviorExtensionElement, WCFMessaging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' registered for extension 'messageBehaviour' could not be loaded.

Source Error:

Line 40: <endpointBehaviors>

Line 41: <behavior name = "MessageEndPointBehaviour">

Line 42: <messageBehaviour />

Line 43: </behavior>

Line 44: </endpointBehaviors>

How to resolve this?

# April 9, 2008 9:58 AM

paolopia said:

x madhavtr. probably you are missing the WCFMessaging.dll assembly in your bin directory, or you mispelled the FQN of the ExtensionElement type.

# May 23, 2008 2:50 AM

vashistha said:

Hi Paolo,

I understood in the way Endpoint behaviors are applied to WCF servcie end points. Tell me one thing what if customer has already applied one Endpoint behavior and another third party provides a new End point behavior to apply to the endpoint how can he do so.

Since in an endpoint one can apply only one Endpoint behavior so how to deploy the second one?

Thanks and Regards

# May 30, 2008 8:01 AM

Phil Bolduc said:

I was looking at your sample. One thing that I question is why you need reassign the request object?

// your code

MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);

       request = buffer.CreateMessage();

       Message originalMessage = buffer.CreateMessage();

// your code

MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);

      // why? request = buffer.CreateMessage();

       Message originalMessage = buffer.CreateMessage();

# July 30, 2008 1:01 AM

philippe lombaers said:

Hi,

Excelent articel.

For logging purposes I'd like to retrieve the reply Message and store it in a Db.

But I 'd like to limit the size of the text I retrieve.

Is this possible ?

Thanks

# August 5, 2008 2:51 AM

RonaldK said:

Very good post and I saw you have some more interesting posts!

Thx!

# August 22, 2008 3:59 AM

Tmeyhoff's Dynamics NAV blog and more said:

When using WCF you sometimes need to intercept message on your communication channel to do various stuff.

# December 9, 2008 7:42 AM

ElBarto said:

Hi...

Do you know any way to remove a intercepted message from the channel?

Example if I try to remove the message with

request.close()

or remove the body this will lead to a exception.

# July 25, 2009 12:45 PM

Vinod said:

I understand the IMessageInspector is not available with Compact Framework 3.5 but is there any way that I could append the custom message headers from the windows mobile client for each call to the service ? I would ideally love to do the same thing that you have done here, for a Compact Framework 3.5 Windows Mobile client. Please help me with this.

# September 23, 2009 3:07 AM

lifegame said:

A small question from a WCF beginner:

I am looking for a method to dump the original transporting message in a SOAP 1.1 MTOM communication, which should contains a SOAP envelope in one MIME part, and binary data in another MIME part.

I tried several methods to dump message, including using messageLogging configuration, and writing a custom behaviour with a custom MessageInspector. However, in the messages dumped by these methods, binary data have already been encoded into base64 text and embeded into the envelope.

Is there any other way to solve this problem? Can I create a proxy on the MTOM MessageEncoder or on the MTOMMessageEncodingBindingElement? Really hope the WCF API can support AOP...

# October 22, 2009 11:07 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)