Message Inspection in WCF

Message Inspectors can be a very usefull tool in diagnosing problems between WCF services and clients.
The messages that are transferred between clients/services can be intercepted and operations performed on them.
We’ve used this at work in conjunction with a tool called SaopUI to capture the SOAP messages and fire them at our service.
This can be usefull for load testing, concurrency testing scenarios amongst others.

What I’ll be demonstrating

Sending a message from our client to the service and receiving a reply.
Capturing the SOAP packets in their raw form on the service, before they are processed by the service operations.

The parts we’ll be going over from my example solution

A service (CoffeeMakingservice)
A client (CoffeeAddict)
The message inspecting components

Service implementation

using System.Threading;

namespace CoffeeAddiction
{
    public class CoffeeShop : ICoffeeShop
    {
        public Coffee MakeCoffee(CoffeeType coffeeType)
        {
            return RunBarrister(coffeeType);
        }

        private Coffee RunBarrister(CoffeeType coffeeType)
        {
            Thread.Sleep(5000);
            return new Coffee() { TypeOfCoffee = coffeeType };
        }
    }
}

Client

using System;
using CoffeeAddict.ServieProxy;

namespace CoffeeAddict
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hi there. What type of Coffee would you like today?");

            ConsoleKey cK;

            do
            {
                DisplayOptionsToAddict();
                ProcessOrder();
                Console.WriteLine("Hit the Escape key to exit the shop. Hit any other key to order more joe.");
                Console.WriteLine();
                cK = Console.ReadKey(true).Key;
            }
            while (cK != ConsoleKey.Escape);
        }

        private static void ProcessOrder()
        {
            Coffee yourCoffee;
            char selectedKey;
            int option;
            bool selectionRequested = false;
            do
            {
                if (selectionRequested)
                    Console.WriteLine("You have entered an invalid option. Please try again.");

                selectedKey = Console.ReadKey(true).KeyChar;
                int.TryParse(selectedKey.ToString(), out option);
                if (!selectionRequested) selectionRequested = true;

            }
            while (option < 1 || option > Enum.GetValues(typeof(CoffeeType)).Length);

            Console.WriteLine("Thank you. We will now process your order.");

            using (CoffeeShopClient coffeeShopClient = new CoffeeShopClient())
                yourCoffee = coffeeShopClient.MakeCoffee((CoffeeType)option);

            Console.WriteLine();
            Console.WriteLine("Your {0} is served!", yourCoffee.TypeOfCoffee);
        }

        private static void DisplayOptionsToAddict()
        {
            Console.WriteLine("Please select the associated number");
            Console.WriteLine();

            foreach (string coffeeType in Enum.GetNames(typeof(CoffeeType)))
            {
                Console.WriteLine("{0} : {1}",
                    Convert.ToInt32(Enum.Parse(typeof(CoffeeType), coffeeType)),
                    coffeeType
                    );
            }
            Console.WriteLine();
        }
    }
}

I’ve hosted these projects in console apps for simplicities sake.
Run the service.

Now generate our ServiceProxy.
You would have to do this before you write the client.

Now we’re ready to start the client.
The client will give a welcome and display a list of options to choose from.
Once the user has made his/her choice, a proxy of the service is created and the users choice is sent to the service for coffee production to begin.
Once the coffee is made, it’s returned to the client.
The client can then decide whether he/she wants another coffee.

——

Now for the message inspection part

As we are going to be inspecting the messages on the service side, we implement the IDispatchMessageInspector.
If we were inspecting the messages on the client side, we would implement the IClientMessageInspector.
As you can see, the methods on these interfaces take a Message parameter.
The Message contains the serialized parameters, operation name, and other information about the message being inspected.
The body of the message is implemented as a streamed object, so we’ve given it some special attention.
Also the body of the message can be processed only once during the lifetime of the Message object.
Any further attempts to retrieve the body, will result in an InvalidOperationException.
The solution to this is to create a copy, by using the CreateBufferedCopy method of the message.
This allows us to create a copy of the original message by using the CreateMessage method on the new MessageBuffer we now have.
As you can see, we write the message out to a time stamped file.

using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.Xml;

namespace MessageListener.Instrumentation
{
    public class MessageInspector : IDispatchMessageInspector
    {
        const string LogDir = @"C:\Logs\CoffeeMakingService\";

        private Message TraceMessage(MessageBuffer buffer)
        {
            //Must use a buffer rather than the origonal message, because the Message's body can be processed only once.
            Message msg = buffer.CreateMessage();

            //Setup StringWriter to use as input for our StreamWriter
            //This is needed in order to capture the body of the message, because the body is streamed.
            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter);
            msg.WriteMessage(xmlTextWriter);
            xmlTextWriter.Flush();
            xmlTextWriter.Close();

            //Setup filename to write to
            if (!Directory.Exists(LogDir))
                Directory.CreateDirectory(LogDir);

            DateTime now = DateTime.Now;
            string datePart = now.Year.ToString() + '-' + now.Month.ToString() + '-' + now.Day.ToString() + '-' + now.Hour + '-' + now.Minute + '-' + now.Second;
            string fileName = LogDir + "\\" + datePart + '-' + "SoapEnv.xml";

            //Write to file
            using (StreamWriter sw = new StreamWriter(fileName))
                sw.Write(stringWriter.ToString());

            //Return copy of origonal message with unalterd State
            return buffer.CreateMessage();
        }

        //BeforeSendReply is called after the response has been constructed by the service operation
        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            //Uncomment the below line if you need to capture the reply message
            //reply = TraceMessage(reply.CreateBufferedCopy(int.MaxValue));
        }

        //The AfterReceiveRequest method is fired after the message has been received but prior to invoking the service operation
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            request = TraceMessage(request.CreateBufferedCopy(int.MaxValue));
            return null;
        }
    }
}

Now we have to add the message inspector to the services channel stack.
First we create the behavior.
Now the only methods we are likely to want to use are the ApplyClientBehavior and/or ApplyDispatchBehavior.
ApplyDispatchBehavior provides the customization we will need to add our new inspector to the collection of message inspectors on the dispatch runtime of our endpoint dispatcher on the service side.

IEndpointBehavior implementation

using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace MessageListener.Instrumentation
{
    public class LoggingEndpointBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {}

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {}

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

        public void Validate(ServiceEndpoint endpoint)
        {}
    }
}

Now we need to add our behavior to a behavior extension.
The BehaviorType property and the CreateBehavior method must be overridden.
By doing this we provide an instance of our behavior when the framework sees that we have added directions to our extension in the configuration.

Sub classing BehaviorExtensionElement

using System;
using System.ServiceModel.Configuration;

namespace MessageListener.Instrumentation
{
    public class LoggingBehaviorExtensionElement : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get
            {
                return typeof(LoggingEndpointBehavior);
            }
        }

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

After the behavior is added to an extension, it can be added to the channel stack.
We tell the framework where to look for our extension, by adding directions to our configuration file.
We now add a reference to our extension (messageInspector) to the endpoint behavior.

Important parts of the services config file.
I’ve left some parts out of this example, to allow us to focus on the listening functionality.

  <system.serviceModel>


























Now when we run the service and the client, we can enjoy the fruits of our labor.

Click on link below to download full source code.

You can grap the MessageListenerDemo code here.

You’ll need Visual Studio 2010 unless you want to create new projects using the source files.

public class WSDualHttpBinding : Binding,...
{
   public Uri ClientBaseAddress
   {get;set;}
   //More members
}

Tags: ,

12 Responses to “Message Inspection in WCF”

  1. Armando Restrepo Says:

    Hello!!! Excellent article you’ve saved my day, I didn’t even know if this was possible

  2. Ranjan Says:

    Hello,

    The sample file is deleted.. can you please provide me the link where I can download?

  3. Vladimir Says:

    Hello binarymist, thanks for the great article … would you be so kind to re-upload full source code in case you still have it ?

  4. binarymist Says:

    Your in luck. Check link above.

  5. Pileggi Says:

    Great!!! Thank tou!!!

  6. hfpg2001 Says:

    Hello thanks for th article but i tried and when i compile it. The vs2010 thorws several errors telling me that i dont have all the needed references.

  7. hfpg2001 Says:

    Hello i just added system.xml.serialization and system.configuration … and wonder compile it weelll… thanks

  8. m ballinger Says:

    Great article – what are the config file updates (seem to be masked in article)

  9. WCF Noob Says:

    Thank you. Wish we could see the code or the config file to fully understand what is going on.

Leave a comment