Sunday, July 15, 2012

WCF Client Request / Response Message Inspection

Very recently I encountered a requirement for inspection of WCF messages passed to and from a service. This feature was required on the client side as the client applications requirement was to store these messages as log entries in the database. Although WCF does not support this out-of-the-box, it was pretty darn easy to implement it just by implementing two (out of many) interfaces in the WFC framework.

Before I go any further I need to mention that this blog post “Capture XML In WCF Service” helped me out a lot, although it talks about the message inspection on the service host itself, where as this post is about message inspection on the client side. I have further made a few enhancements over the code. So lets get started.

Intercepting WCF messages

In order to inspect client messages going in and coming out of the client we need to implement the interface contract IClientMessageInspector, as seen below,

///
/// Class to perform custome message inspection as behaviour.
/// 
public class MessageInspectorBehavior : IClientMessageInspector
{
    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        // Do nothing.
    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        return null;
    }
}

Upon attaching this to the client runtime, WCF will ensure the two methods listed above is called when a request is sent to and a response is received from the service. So here is where we will write our custom code which will see later in this post.

The next important point is that we need to attach this inspector to the client runtime, and this can be done by the creating our own custom service behavior by implementing the IEndpointBehavior interface, as seen below (Note that I have implemented the interface to the same class that implements the IClientMessageInspector interface),

///
/// Class to perform custome message inspection as behaviour.
/// 
public class MessageInspectorBehavior : IClientMessageInspector, IEndpointBehavior
{
    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        // Do nothing.
    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        return null;
    }

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

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        // Do nothing.
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        // Do nothing.
    }

    public void Validate(ServiceEndpoint endpoint)
    {
        // Do nothing.
    }
}

Next is that we need to integrate the message inspector to the behavior and this is done in the ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) method as seen below,

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
    // Add the message inspector to the as part of the service behaviour.
    clientRuntime.MessageInspectors.Add(this);
}

Now we have a custom inspector attached to a custom behavior, so how do we get the the request and response messages out of the inspector for logging? Well there are few ways to do this, but my preference was to embed an event handler in the inspector so users can subscribe to if required and be notified of when a request or response message is inspected. Here is the code that does just that,

///
/// Class to perform custome message inspection as behaviour.
/// 
public class MessageInspectorBehavior : IClientMessageInspector, IEndpointBehavior
{
    // Acts as the event to notify subscribers of message inspection.
    public event EventHandler OnMessageInspected;

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        if (OnMessageInspected != null)
        {
            // Notify the subscribers of the inpected message.
            OnMessageInspected(this, new MessageInspectorArgs { Message = reply.ToString(), MessageInspectionType = eMessageInspectionType.Response });
        }
    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        if (OnMessageInspected != null)
        {
            // Notify the subscribers of the inpected message.
            OnMessageInspected(this, new MessageInspectorArgs { Message = request.ToString(), MessageInspectionType = eMessageInspectionType.Response });
        }
        return null;
    }

   // Rest of the class code...

}

MessageInspectionArgs class and eMessageInspectionType enum are custom implementations for passing event arguments to the users for identifying the event related information. The code for these definitions are as seen below,

///
/// Enum representing message inspection types.
/// 
public enum eMessageInspectionType { Request = 0, Response = 1 };

///
/// Class to pass inspection event arguments.
/// 
public class MessageInspectorArgs : EventArgs
{
    ///
    /// Type of the message inpected.
    /// 
    public eMessageInspectionType MessageInspectionType { get; internal set; }

    /// 
    /// Inspected raw message.
    /// 
    public string Message { get; internal set; }
}

Finally its time for integrating it to the client application. Listed below is the code for that. Its pretty concise and easy to implement with very little or no effort.

class Program
{
    static void Main(string[] args)
    {
        string request = string.Empty;
        string response = string.Empty;

        // Instantiate the service.
        ServiceClient sc = new ServiceClient();

        // Instanticate the custom inspector behaviour.
        MessageInspectorBehavior cb = new MessageInspectorBehavior();

        // Add the custom behaviour to the list of service behaviours.
        sc.Endpoint.Behaviors.Add(cb);

        // Subscribe to message inpection events and provess the event invokation.
        cb.OnMessageInspected += (src, e) =>
        {
            if (e.MessageInspectionType == eMessageInspectionType.Request) request = e.Message;
            else response = e.Message;
        };

        // Call the service.
        var x = sc.GetData(1);

        // Display or log the results.
        Console.WriteLine(string.Format("Request\nMessage: {0}\n\nResponse\nMessage: {1}", request, response));

        Console.ReadKey();
    }
}

You can download the sample code from here. Let me know your feedback on suggestions or even improvements for that matter.

Comments

5 comments:

About Me

I am a software developer with over 7+ years of experience, particularly interested in distributed enterprise application development where my focus is on development with the usage of .Net, Java and any other technology that fascinate me.