WCF Extensibility – Message Inspectors

This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page.

The message inspectors are probably the most used extensibility points of the WCF runtime. Anytime you need to log, inspect, modify or completely replace a message, the two inspector interfaces (IClientMessageInspector for the client side; IDispatchMessageInspector for the server side) are likely to be perfect candidates for the solution. There are so many examples of the dispatch inspector (and also many more examples using client inspectors) being used as solution for problems in the forums that it’s actually hard to pick one scenario to show in this post. I’ll then show one full scenario and briefly mention others.

Public implementations in WCF

None. Most of the runtime extensibility points don’t have any public implementations, although there are a few internal ones for the message inspectors.

Interface declarations

  1. public interface IDispatchMessageInspector
  2. {
  3.     object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
  4.     void BeforeSendReply(ref Message reply, object correlationState);
  5. }
  7. public interface IClientMessageInspector
  8. {
  9.     void AfterReceiveReply(ref Message reply, object correlationState);
  10.     object BeforeSendRequest(ref Message request, IClientChannel channel);
  11. }

The inspector interfaces have two methods: one after the incoming message is received on the wire and before it’s dispatched to the application (AfterReceive), and one after the outgoing message is received from the application and before it’s encoded to be sent on the wire (BeforeSend). Let’s talk about them individually.

At the server side, implementations of IDispatchMessageInspector get a chance to inspect the incoming message right after the request is received via the AfterReceiveRequest method. Besides the message, the method is also passed the channel through which the message arrived (which contains information about any current sessions, local and remote address, and so on), and the instance context associated with the service (with references to the host, and any extensions associated with it). After the user code on the service operation processed the request, and the reply is created into a Message object, BeforeSendReply is called on the inspector code, with the message object and the correlation state, which is whatever AfterReceiveRequest returned – this way the code can correlate the two parts of the message inspection for a single client request.

One thing that deserves mention is that the Message object to both methods is passed by reference. WCF Message objects can only be “consumed” once – and “consumed” can mean read, written or copied. The message body is essentially a read-once stream, so once it’s consumed it cannot be used again. So if, in the inspector code, one were to read the message, the WCF runtime wouldn’t be able to reuse that message in the rest of its pipeline (i.e., to encode it to send as a reply or to parse it into operation parameters). So if the inspector code needs to read the message, it’s the responsibility of the inspector to recreate the message. The code below shows one way of recreating the message object.

  1. public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
  2. {
  3.     Console.WriteLine("Message:");
  4.     Console.WriteLine(request);
  5.     Console.WriteLine();
  6.     MemoryStream ms = new MemoryStream();
  7.     XmlWriter writer = XmlWriter.Create(ms);
  8.     request.WriteMessage(writer); // the message was consumed here
  9.     writer.Flush();
  10.     ms.Position = 0;
  11.     XmlDocument xmlDoc = new XmlDocument();
  12.     xmlDoc.Load(ms);
  13.     this.ChangeMessage(xmlDoc);
  15.     //Now recreating the message
  16.     ms = new MemoryStream();
  17.     xmlDoc.Save(ms);
  18.     ms.Position = 0;
  19.     XmlReader reader = XmlReader.Create(ms);
  20.     Message newMessage = Message.CreateMessage(reader, int.MaxValue, request.Version);
  21.     newMessage.Properties.CopyProperties(request.Properties);
  22.     request = newMessage;
  24.     return null;

The list of method calls which would cause a message to be consumed are the following: CreateBufferedCopy, GetBody, GetReaderAtBodyContents, WriteBody, WriteBodyContents and WriteMessage. All others are fine: accessing the message headers is ok, since they’re always buffered, so that doesn’t “invalidate” the message. Accessing the message properties is also safe, as they’re just a named dictionary (buffered). Finally, calling Message.ToString in the message is also safe – if the message body is indeed a read-once stream, it will be represented simply as “... stream ...”. If the message is buffered it’s actually possible that the value returned by ToString will display the whole message body.

At the client side, IClientMessageInspector implementations get a chance to inspect the message after it’s been created based on the operation call, and right before it’s encoded. BeforeSendRequest receives the message (again, passed by reference) and the channel through which the message is being sent. When the response from the server arrives and is decoded (by the message encoder) into a Message object, it’s passed to the client inspector in its AfterReceiveReply method. Like in the server version, the method receives the message object (again, by reference) and the correlation state returned by the BeforeSendRequest method. Notice that if the operation contract is defined as one way, the call to AfterReceiveReply is not made at the client inspector, which makes sense, since one way operations do not have a reply message. Weirdly enough, for dispatch message inspectors, BeforeSendReply is actually called for HTTP, but the reply message passed to it is null.

How to add message inspectors

At the server side: the list of dispatch message inspectors is available at the DispatchRuntime object. The object is typically accessed via the endpoint dispatcher in a call to IEndpointBehavior.ApplyDispatchBehavior, or directly passed to an implementation of IContractBehavior.ApplyDispatchBehavior.

  1. public class MyContractBehavior : IContractBehavior
  2. {
  3.     public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
  4.     {
  5.         dispatchRuntime.MessageInspectors.Add(new MyInspector());
  6.     }
  7. }
  8. public class MyEndpointBehavior : IEndpointBehavior
  9. {
  10.     public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
  11.     {
  12.         endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyInspector());
  13.     }
  14. }

At the client side: the list of client message inspectors is available at the ClientRuntime object.

  1. public class MyContractBehavior : IContractBehavior
  2. {
  3.     public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  4.     {
  5.         clientRuntime.MessageInspectors.Add(new MyClientInspector());
  6.     }
  7. }
  8. public class MyEndpointBehavior : IEndpointBehavior
  9. {
  10.     public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  11.     {
  12.         clientRuntime.MessageInspectors.Add(new MyClientInspector());
  13.     }
  14. }


Real world scenario: Inspecting non-XML messages

As I mentioned before, all WCF messages are XML-based – the message envelope is an XML Infoset represented in memory, with the (optional) header and the body as child XML elements of the envelope. A message can be directly written to a XML writer, and created based on a XML reader. This works quite well in the XML (including SOAP and POX) world. However, with the introduction of the WCF HTTP programming model in .NET Framework 3.5, WCF started accepting out-of-the-box more types of content (most notably JSON and binary content). But since the whole WCF stack is XML-based, sometimes the behavior of the messages can be counter-intuitive.

Take, for example, a simple message logger, which is typically implemented using the message inspectors described in this post. Let’s say that we have a contact manager, which is implemented as a bunch of operations used primarily by web pages (thus using JSON as the primary message format):

  1. [DataContract]
  2. public class Contact
  3. {
  4.     [DataMember]
  5.     public string Id { get; set; }
  6.     [DataMember]
  7.     public string Name { get; set; }
  8.     [DataMember]
  9.     public string Email { get; set; }
  10.     [DataMember]
  11.     public string[] Telephones { get; set; }
  12. }
  14. [ServiceContract]
  15. public interface IContactManager
  16. {
  17.     [WebInvoke(
  18.         Method = "POST",
  19.         UriTemplate = "/Contacts",
  20.         ResponseFormat = WebMessageFormat.Json)]
  21.     string AddContact(Contact contact);
  23.     [WebInvoke(
  24.         Method = "PUT",
  25.         UriTemplate = "/Contacts/{id}",
  26.         ResponseFormat = WebMessageFormat.Json)]
  27.     void UpdateContact(string id, Contact contact);
  29.     [WebInvoke(
  30.         Method = "DELETE",
  31.         UriTemplate = "/Contacts/{id}",
  32.         ResponseFormat = WebMessageFormat.Json)]
  33.     void DeleteContact(string id);
  35.     [WebGet(UriTemplate = "/Contacts", ResponseFormat = WebMessageFormat.Json)]
  36.     List<Contact> GetAllContacts();
  38.     [WebGet(UriTemplate = "/Contacts/{id}", ResponseFormat = WebMessageFormat.Json)]
  39.     Contact GetContact(string id);
  41.     [WebGet(UriTemplate = "/ContactsAsText")]
  42.     Stream GetContactsAsText();
  43. }

Now let’s say we send the following request to the service:

POST /Contacts HTTP/1.1
Content-Type: application/json
Host: my.host.name.com
Content-Length: 90
Expect: 100-continue

{"Name":"Jane Roe", "Email":"jane@roe.com", "Telephones":["202-555-4444", "202-555-8888"]}

Inside the message inspector, we have a simple implementation which prints the message content:

  1. public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
  2. {
  3.     if (!request.IsEmpty)
  4.     {
  5.         Console.ForegroundColor = ConsoleColor.Green;
  6.         Console.WriteLine("Incoming message:");
  7.         Console.WriteLine(request);
  8.         Console.ResetColor();
  9.     }
  11.     return null;
  12. }

Easy, right? However, we’re in the Message world, and that means XML, so instead of seeing the nice JSON request which was sent to the server, what ends up being printed is the message below:

<root type="object">
  <Name type="string">Jane Roe</Name>
  <Email type="string">jane@roe.com</Email>
  <Telephones type="array">
    <item type="string">202-555-4444</item>
    <item type="string">202-555-8888</item>

This throws quite a few people off-balance. What is being printed out is actually equivalent to the incoming JSON, by following the mapping between JSON and XML used in WCF. But that doesn’t help for all the scenarios where one needs to log incoming messages, or even change the JSON in the message. The same would happen if the message was a “raw” message, for operations in which the return type or the operation parameter was of type Stream – see more information on the WCF raw programming model for returning or receiving raw data – what would be printed would be the XML mapping of raw data (the base64binary data wrapped around a <Binary> XML element).

This example will show then how to read such content in a way that can be easily manipulated (the example simply logs it to a file, but it can easily be modified to change the message on the fly as well). And before I go further, the usual disclaimer – this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few contracts and it worked, but I cannot guarantee that it will work for all scenarios (please let me know if you find a bug or something missing). Also, for simplicity sake it doesn’t have a lot of error handling which a production-level code would. Also, the contact manager is all stored in memory, a “real” one would have a backing database or something more “persistent”.

First, for completeness sake, the service which implements the contract shown before, which is shown below. The implementation is simple (all in memory), with a lock around operations with the “repository”.

  1. public class ContactManagerService : IContactManager
  2. {
  3.     static List<Contact> AllContacts = new List<Contact>();
  4.     static int currentId = 0;
  5.     static object syncRoot = new object();
  7.     public string AddContact(Contact contact)
  8.     {
  9.         int contactId = Interlocked.Increment(ref currentId);
  10.         contact.Id = contactId.ToString(CultureInfo.InvariantCulture);
  11.         lock (syncRoot)
  12.         {
  13.             AllContacts.Add(contact);
  14.             WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Created;
  15.         }
  17.         return contact.Id;
  18.     }
  20.     public void UpdateContact(string id, Contact contact)
  21.     {
  22.         contact.Id = id;
  23.         lock (syncRoot)
  24.         {
  25.             int index = this.FetchContact(id);
  26.             if (index >= 0)
  27.             {
  28.                 AllContacts[index] = contact;
  29.             }
  30.         }
  31.     }
  33.     public void DeleteContact(string id)
  34.     {
  35.         lock (syncRoot)
  36.         {
  37.             int index = this.FetchContact(id);
  38.             if (index >= 0)
  39.             {
  40.                 AllContacts.RemoveAt(index);
  41.             }
  42.         }
  43.     }
  45.     public List<Contact> GetAllContacts()
  46.     {
  47.         List<Contact> result;
  48.         lock (syncRoot)
  49.         {
  50.             result = AllContacts.ToList();
  51.         }
  53.         return result;
  54.     }
  56.     public Contact GetContact(string id)
  57.     {
  58.         Contact result;
  59.         lock (syncRoot)
  60.         {
  61.             int index = this.FetchContact(id);
  62.             result = index < 0 ? null : AllContacts[index];
  63.         }
  65.         return result;
  66.     }
  68.     public Stream GetContactsAsText()
  69.     {
  70.         StringBuilder sb = new StringBuilder();
  71.         WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
  72.         lock (syncRoot)
  73.         {
  74.             foreach (var contact in AllContacts)
  75.             {
  76.                 sb.AppendLine("Contact " + contact.Id + ":");
  77.                 sb.AppendLine("  Name: " + contact.Name);
  78.                 sb.AppendLine("  Email: " + contact.Email);
  79.                 sb.AppendLine("  Telephones:");
  80.                 foreach (var phone in contact.Telephones)
  81.                 {
  82.                     sb.AppendLine("    " + phone);
  83.                 }
  84.             }
  85.         }
  87.         WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain; charset=utf-8";
  88.         MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString()));
  89.         return ms;
  90.     }
  92.     private int FetchContact(string id)
  93.     {
  94.         int result = -1;
  95.         for (int i = 0; i < AllContacts.Count; i++)
  96.         {
  97.             if (AllContacts[i].Id == id)
  98.             {
  99.                 result = i;
  100.                 break;
  101.             }
  102.         }
  104.         if (result < 0)
  105.         {
  106.             WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
  107.         }
  109.         return result;
  110.     }
  111. }

Now for the inspector itself. I’ll implement it as both an IDispatchMessageInspector and an IEndpointBehavior to make it easier for adding it to the endpoint I’m interested in logging the messages for. It will contain a folder where to log the messages, plus a counter to create the file name where the messages will be logged. The IEndpointBehavior implementation is simple, only using the ApplyDispatchBehavior method to add that instance to the list of message inspectors in the dispatch runtime.

  1. public class IncomingMessageLogger : IDispatchMessageInspector, IEndpointBehavior
  2. {
  3.     const string MessageLogFolder = @"c:\temp\";
  4.     static int messageLogFileIndex = 0;
  6.     public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
  7.     {
  8.     }
  10.     public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  11.     {
  12.     }
  14.     public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
  15.     {
  16.         endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
  17.     }
  19.     public void Validate(ServiceEndpoint endpoint)
  20.     {
  21.     }
  22. }

Now for the message inspector implementation. For every incoming or outgoing message, we’ll create a new file in the folder defined in the const field for the class.

  1. public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
  2. {
  3.     string messageFileName = string.Format("{0}Log{1:000}_Incoming.txt", MessageLogFolder, Interlocked.Increment(ref messageLogFileIndex));
  4.     Uri requestUri = request.Headers.To;
  5.     using (StreamWriter sw = File.CreateText(messageFileName))
  6.     {
  7.         HttpRequestMessageProperty httpReq = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
  9.         sw.WriteLine("{0} {1}", httpReq.Method, requestUri);
  10.         foreach (var header in httpReq.Headers.AllKeys)
  11.         {
  12.             sw.WriteLine("{0}: {1}", header, httpReq.Headers[header]);
  13.         }
  15.         if (!request.IsEmpty)
  16.         {
  17.             sw.WriteLine();
  18.             sw.WriteLine(this.MessageToString(ref request));
  19.         }
  20.     }
  22.     return requestUri;
  23. }
  25. public void BeforeSendReply(ref Message reply, object correlationState)
  26. {
  27.     string messageFileName = string.Format("{0}Log{1:000}_Outgoing.txt", MessageLogFolder, Interlocked.Increment(ref messageLogFileIndex));
  29.     using (StreamWriter sw = File.CreateText(messageFileName))
  30.     {
  31.         sw.WriteLine("Response to request to {0}:", (Uri)correlationState);
  32.         HttpResponseMessageProperty httpResp = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
  33.         sw.WriteLine("{0} {1}", (int)httpResp.StatusCode, httpResp.StatusCode);
  35.         if (!reply.IsEmpty)
  36.         {
  37.             sw.WriteLine();
  38.             sw.WriteLine(this.MessageToString(ref reply));
  39.         }
  40.     }
  41. }

Now for the interesting part – the implementation of MessageToString. Incoming messages from the encoder used in the WebHttpBinding are tagged with a property of type WebBodyFormatMessageProperty, which defines which of the inner encoders was used to decode the message (the encoder from that binding is actually composed of three encoders: one for XML content, one for JSON, and one for everything else). That property is also used on outgoing messages to tell the web encoder which of the inner encoders should be used to write the message to the wire. So we’ll define a small helper method to retrieve the format from the message object.

  1. private WebContentFormat GetMessageContentFormat(Message message)
  2. {
  3.     WebContentFormat format = WebContentFormat.Default;
  4.     if (message.Properties.ContainsKey(WebBodyFormatMessageProperty.Name))
  5.     {
  6.         WebBodyFormatMessageProperty bodyFormat;
  7.         bodyFormat = (WebBodyFormatMessageProperty)message.Properties[WebBodyFormatMessageProperty.Name];
  8.         format = bodyFormat.Format;
  9.     }
  11.     return format;
  12. }

And now for MessageToString (really this time). For XML and JSON messages, the implementation will write the message into a XmlWriter of the appropriate type. The writer created by the class JsonReaderWriterFactory implements the mapping between JSON and XML I mentioned before, so we’ll use it for JSON messages; for XML messages (or for messages which don’t have the body format property) we’ll use the “normal” XML writer from WCF; for raw messages we’ll deal with them specifically in a separate method. After the message is written, it has been consumed, so we need to recreate it to pass it along the channel stack. Using a reader of the same type and creating a new message using the Message.CreateMessage(XmlDictionaryReader, int, MessageVersion) overload and copying the original message properties (which are not serialized when the message is written out).

For the raw messages, since the format is relatively simple (the binary data, written as base64Binary data, wrapped in a single <Binary> element), we can consume it directly – read the message body, skip the wrapping element then read the whole body at once. In this case I’m always converting the binary data to text, in the general case that may not work (if the binary data is an image, for example), but that’s beyond the scope for this post.

  1. private string MessageToString(ref Message message)
  2. {
  3.     WebContentFormat messageFormat = this.GetMessageContentFormat(message);
  4.     MemoryStream ms = new MemoryStream();
  5.     XmlDictionaryWriter writer = null;
  6.     switch (messageFormat)
  7.     {
  8.         case WebContentFormat.Default:
  9.         case WebContentFormat.Xml:
  10.             writer = XmlDictionaryWriter.CreateTextWriter(ms);
  11.             break;
  12.         case WebContentFormat.Json:
  13.             writer = JsonReaderWriterFactory.CreateJsonWriter(ms);
  14.             break;
  15.         case WebContentFormat.Raw:
  16.             // special case for raw, easier implemented separately
  17.             return this.ReadRawBody(ref message);
  18.     }
  20.     message.WriteMessage(writer);
  21.     writer.Flush();
  22.     string messageBody = Encoding.UTF8.GetString(ms.ToArray());
  24.     // Here would be a good place to change the message body, if so desired.
  26.     // now that the message was read, it needs to be recreated.
  27.     ms.Position = 0;
  29.     // if the message body was modified, needs to reencode it, as show below
  30.     // ms = new MemoryStream(Encoding.UTF8.GetBytes(messageBody));
  32.     XmlDictionaryReader reader;
  33.     if (messageFormat == WebContentFormat.Json)
  34.     {
  35.         reader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max);
  36.     }
  37.     else
  38.     {
  39.         reader = XmlDictionaryReader.CreateTextReader(ms, XmlDictionaryReaderQuotas.Max);
  40.     }
  42.     Message newMessage = Message.CreateMessage(reader, int.MaxValue, message.Version);
  43.     newMessage.Properties.CopyProperties(message.Properties);
  44.     message = newMessage;
  46.     return messageBody;
  47. }
  49. private string ReadRawBody(ref Message message)
  50. {
  51.     XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
  52.     bodyReader.ReadStartElement("Binary");
  53.     byte[] bodyBytes = bodyReader.ReadContentAsBase64();
  54.     string messageBody = Encoding.UTF8.GetString(bodyBytes);
  56.     // Now to recreate the message
  57.     MemoryStream ms = new MemoryStream();
  58.     XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(ms);
  59.     writer.WriteStartElement("Binary");
  60.     writer.WriteBase64(bodyBytes, 0, bodyBytes.Length);
  61.     writer.WriteEndElement();
  62.     writer.Flush();
  63.     ms.Position = 0;
  64.     XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max);
  65.     Message newMessage = Message.CreateMessage(reader, int.MaxValue, message.Version);
  66.     newMessage.Properties.CopyProperties(message.Properties);
  67.     message = newMessage;
  69.     return messageBody;
  70. }

That’s it. With this inspector we can log messages of all types for REST services, in their original format. Another way to implement it would be in a custom message encoder (coming up in this series), in which you can have access to the raw bytes coming from the wire, as well as the content-type of the HTTP request.

Now for some test code which sets up the service with that inspector, and sends some messages to it.

  1.     public class Program
  2.     {
  3.         public static void Main(string[] args)
  4.         {
  5.             string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
  6.             ServiceHost host = new ServiceHost(typeof(ContactManagerService), new Uri(baseAddress));
  7.             ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IContactManager), new WebHttpBinding(), "");
  8.             endpoint.Behaviors.Add(new WebHttpBehavior());
  9.             endpoint.Behaviors.Add(new IncomingMessageLogger());
  10.             host.Open();
  11.             Console.WriteLine("Host opened");
  13.             string johnId = SendRequest(
  14.                 "POST",
  15.                 baseAddress + "/Contacts",
  16.                 "application/json",
  17.                 CreateJsonContact(null, "John Doe", "john@doe.com", "206-555-3333"));
  18.             string janeId = SendRequest(
  19.                 "POST",
  20.                 baseAddress + "/Contacts",
  21.                 "application/json",
  22.                 CreateJsonContact(null, "Jane Roe", "jane@roe.com", "202-555-4444 202-555-8888"));
  24.             Console.WriteLine("All contacts");
  25.             SendRequest("GET", baseAddress + "/Contacts", null, null);
  27.             Console.WriteLine("Updating Jane");
  28.             SendRequest(
  29.                 "PUT",
  30.                 baseAddress + "/Contacts/" + janeId,
  31.                 "application/json",
  32.                 CreateJsonContact(janeId, "Jane Roe", "jane@roe.org", "202-555-4444 202-555-8888"));
  34.             Console.WriteLine("All contacts, text format");
  35.             SendRequest("GET", baseAddress + "/ContactsAsText", null, null);
  37.             Console.WriteLine("Deleting John");
  38.             SendRequest("DELETE", baseAddress + "/Contacts/" + johnId, null, null);
  40.             Console.WriteLine("Is John still here?");
  41.             SendRequest("GET", baseAddress + "/Contacts/" + johnId, null, null);
  43.             Console.WriteLine("It also works with XML payloads:");
  44.             string xmlPayload = @"<Contact>
  45.   <Email>johnjr@doe.com</Email>
  46.   <Name>John Doe Jr</Name>
  47.   <Telephones xmlns:a=""http://schemas.microsoft.com/2003/10/Serialization/Arrays"">
  48.     <a:string>333-333-3333</a:string>
  49.   </Telephones>
  50. </Contact>";
  51.             SendRequest(
  52.                 "POST",
  53.                 baseAddress + "/Contacts",
  54.                 "text/xml",
  55.                 xmlPayload);
  57.             Console.WriteLine("All contacts:");
  58.             SendRequest("GET", baseAddress + "/Contacts", null, null);
  59.         }
  61.         static string SendRequest(string method, string uri, string contentType, string body)
  62.         {
  63.             HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
  64.             req.Method = method;
  65.             if (contentType != null)
  66.             {
  67.                 req.ContentType = contentType;
  68.             }
  70.             if (body != null)
  71.             {
  72.                 byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
  73.                 Stream reqStream = req.GetRequestStream();
  74.                 reqStream.Write(bodyBytes, 0, bodyBytes.Length);
  75.                 reqStream.Close();
  76.             }
  78.             HttpWebResponse resp;
  79.             try
  80.             {
  81.                 resp = (HttpWebResponse)req.GetResponse();
  82.             }
  83.             catch (WebException e)
  84.             {
  85.                 resp = (HttpWebResponse)e.Response;
  86.             }
  88.             Console.ForegroundColor = ConsoleColor.Cyan;
  89.             Console.WriteLine("Response to request to {0} - {1}", method, uri);
  90.             Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
  91.             foreach (var headerName in resp.Headers.AllKeys)
  92.             {
  93.                 Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
  94.             }
  96.             Stream respStream = resp.GetResponseStream();
  97.             string result = null;
  98.             if (respStream != null)
  99.             {
  100.                 result = new StreamReader(respStream).ReadToEnd();
  101.                 Console.WriteLine(result);
  102.             }
  104.             Console.WriteLine();
  105.             Console.WriteLine("  -*-*-*-*-*-*-*-*");
  106.             Console.WriteLine();
  108.             Console.ResetColor();
  110.             // Removing the string markers from results (for contact ids)
  111.             if (result.StartsWith("\"") && result.EndsWith("\""))
  112.             {
  113.                 result = result.Substring(1, result.Length - 2);
  114.             }
  116.             return result;
  117.         }
  119.         static string CreateJsonContact(string id, string name, string email, string telephones)
  120.         {
  121.             string[] phoneNumbers = telephones.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  122.             StringBuilder sb = new StringBuilder();
  123.             sb.Append('{');
  124.             if (id != null)
  125.             {
  126.                 sb.AppendFormat("\"Id\":\"{0}\", ", id);
  127.             }
  129.             sb.AppendFormat("\"Name\":\"{0}\", ", name);
  130.             sb.AppendFormat("\"Email\":\"{0}\", ", email);
  131.             sb.Append("\"Telephones\":[");
  132.             for (int i = 0; i < phoneNumbers.Length; i++)
  133.             {
  134.                 if (i > 0) sb.Append(", ");
  135.                 sb.AppendFormat("\"{0}\"", phoneNumbers[i]);
  136.             }
  138.             sb.Append(']');
  139.             sb.Append('}');
  140.             return sb.ToString();
  141.         }
  142.     }

One more thing: we had to deal with lots of different formats and translation between a “XML” message into its own format, which is not something very natural. Some good news is that among the new features coming in an upcoming version of WCF is a new HTTP pipeline, which will make it easier to implement a scenario such as this. And you can actually start using them right now, as there is a preview of the feature in the WCF Codeplex site at http://wcf.codeplex.com.

Coming up

The other inspector interface: IParameterInspector.

[Code in this post]

[Back to the index]

Comments (20)

  1. Brian says:

    I just wanted to chime in and let you know that these blog posts have been excellent.  I've found them very valuable, explanations and the simple demo's have been great.

    Thank You

  2. bohebolo says:

    Very nice article.

    By the way I have some questions, which are:

    1. Is IDispatchMessageInspector.AfterReceiveRequest thread safe?
    2. How to pass an object from it to the operation/method called?


  3. @Brian, thanks!

    @bohebolo, here you go:

    1. No, it's not. If the implementation of AfterReceiveRequest takes some time and multiple clients call the service at once it's possible that there will be multiple threads calling AfterReceiveRequest at the same time.
    2. You can use the message properties, which is essentially a dictionary (keyed by string) of objects which are attached to a message object. On the operation, you'd retrieve the object using the operation context, as shown below.


             public interface ITest



                 int Add(int x, int y);


             public class Service : ITest


                 public int Add(int x, int y)


                     if (OperationContext.Current.IncomingMessageProperties.ContainsKey("MyPropertyName"))


                         Console.WriteLine("Property value: {0}", OperationContext.Current.IncomingMessageProperties["MyPropertyName"]);


                     return x + y;



             public class MyInspector : IEndpointBehavior, IDispatchMessageInspector



                 public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)




                 public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)


                     request.Properties.Add("MyPropertyName", "the property value");

                     return null;


                 public void BeforeSendReply(ref Message reply, object correlationState)




  4. Carlos' blog says:

    This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page .

    Message formatters are the component which do the translation between CLR operations and the WCF Message object – their role is to convert all the operation parameters and return values (possibly via serialization) into a Message on output, and deconstruct the message into parameter and return values on input. Anytime a new format is to be used (be it a new serialization format for currently supported CLR types, or supporting a new CLR type altogether), a message formatter is the interface you’d implement to enable this scenario. For example, even though the type System.IO.Stream is not serializable (it’s even abstract), WCF allows it to be used as the input or return values for methods (for example, in the WCF REST Raw programming model ) and a formatter deals with converting it into messages. Like the message inspectors, there are two versions, one for the server side ( IDispatchMessageFormatter ), and one for the client side ( IClientMessageFormatter ).

    Among the extensibility points listed in this series, the message formatters are the first kind to be required in the WCF pipeline – a service does not need to have any service / endpoint / contract / operation behavior, nor any message / parameter inspector. If the user doesn’t add any of those, the client / service will just work (some behaviors are automatically added to the operations when one adds an endpoint, but that’s an implementation detail for WCF – they aren’t strictly necessary). Formatters, on the other hand, are required (to bridge the gap between the message world and the operations using CLR types). If we don’t add any behaviors to the service / endpoint / contract / operation that sets a formatter, WCF will add one formatter to the operation (usually via the DataContractSerializerOperationBehavior , which is added by default in all operation contracts).

    Public implementations in WCF

    None. As with most of the runtime extensibility points, there are no public implementations of either the dispatch or the client message formatter. There are a lot of internal implementations, though, such as a formatter which converts operation parameters into a message (both using the DataContractSerializer and the XmlSerializer), formatters which map parameters to the HTTP URI (used in REST operations with UriTemplate), among others.

    Interface declaration

    public interface IDispatchMessageFormatter


    void DeserializeRequest( Message message, object [] parameters);

    Message SerializeReply( MessageVersion messageVersion, object [] parameters, object result);


    public interface IClientMessageFormatter


    Message SerializeRequest( MessageVersion messageVersion, object [] parameters);

    object DeserializeReply( Message message, object [] parameters);


    The formatter interfaces each have two methods, one to convert between input outgoing CLR objects into the Message object (serialize), and one to take the incoming message object apart and break it down into incoming CLR objects (deserialize).

    At the server side, after the the message was decoded, passed through the channel stack and other extensibility points and is about to be dispatched to the service operation, the method DeserializeRequest is called to deserialize the message object into an array of parameters to be passed to the operation. The object argument “parameters” will have been allocated, so the implementation of the formatter only needs to read the message and set the operation parameters in their correct order in the array passed to it. After the operation is invoked (and any parameter inspector has a chance to change the operation outputs), SerializeReply is cal

  5. Ross says:

    I'm trying to follow this as closely as possible with my own code, but no messages (requests or replies) seem to have the WebBodyFormatMessageProperty. (Even when the body of the inspector methods are empty.) The inspector gets invoked, but it only has the 3 properties "AllowOutputBatching", "ActivityId" and "httpResponse". Something seems significantly wrong here. Any ideas?

  6. Ross says:

    I think I've resolved the problem. If the contract is decorated with a ResponceFormat such as JSON, then the "WebBodyFormatMessageProperty" is present in the reply properties. If it's decorated with WebMessageFormat.Xml, the property is not in the Message object.

  7. Ross says:

    I've added some information as 'community content.' Carlos, it would be great if you could chime in on this. I spent about a day and half on this. 🙁


  8. Right Ross, you're correct. The default value for the web encoder is Xml (after all it has to choose one encoder to write a message to the wire eventually). If that property is present, then the web encoder will use the format specified on it; otherwise it will choose XML.

  9. Ross says:

    Thanks Carlos! I kept looking for the property but couldn't figure out why it was missing. 🙂

  10. Mohamad says:

    Hi, I downloaded the code and ran the sample. All is great, except that the "request" parameter of "AfterReceiveRequest" is always empty "request.IsEmpty = true" for Get operations. Can you please explain why is that? I want to have an inspector that logs all operations including Get… Thanks in advance

  11. Hi Mohamad, that means that the request does not have a body (which is the case for GET requests). For such requests, the information is actually in the request header (URI and HTTP headers). To access the HTTP headers, you can use the HttpRequestMessageProperty property from the request, and to get the URI you can access the To header. The code below shows an example of that.

       public class BlogPost_MessageInspectors_20111226



           public class Service



               public int AddGet(int x, int y) { return x + y; }

               [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]

               public int AddPost(int x, int y) { return x + y; }


           class MyBehavior : WebHttpBehavior, IDispatchMessageInspector


               public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)


                   base.ApplyDispatchBehavior(endpoint, endpointDispatcher);



               public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)



                   HttpRequestMessageProperty prop = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;

                   foreach (var header in prop.Headers.AllKeys)


                       Console.WriteLine("{0}: {1}", header, prop.Headers[header]);


                   return null;


               public void BeforeSendReply(ref Message reply, object correlationState)




           public static void Test()


               string baseAddress = "http://&quot; + Environment.MachineName + ":8000/Service";

               ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));

               host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "").Behaviors.Add(new MyBehavior());


               Console.WriteLine("Host opened");

               WebClient c = new WebClient();

               Console.WriteLine(c.DownloadString(baseAddress + "/AddGet?x=2&y=5"));

               Console.Write("Press ENTER to close the host");





  12. Mohamad says:

    Awesome! Many thanks. Your posts are helping my entire organization make use of RESTful WCF…

  13. Faheem says:

    I was getting

    "Manual addressing is enabled on this factory, so all messages sent must be pre-addressed."

    in POST requests since my To header was lost during creation of new message. I added following line to fix the issue

    Message newMessage = Message.CreateMessage(reader, int.MaxValue, request.Version);


    ===> newMessage.Headers.CopyHeadersFrom(message);

    request = newMessage;

    Also for some reason an extra property of AllowOutputBatching is added to the new message.

  14. Hi Faheem, do you get the error when running the sample out of the code gallery? I just downloaded it into a new machine, and running it with F5 worked just fine. Or are you changing the sample? Thanks!

  15. Faheem says:

    Carlos, I copied over the inspector class and added behavior on my endpoint via code. Also switched logging to log4net.

    The difference that I think could be is that I am adding WebHttpBehavior programmatically and that might have changed the order in which behaviors are applied. If WebHttpBehavior is kicked-in before inspector then we have to copy headers as well. What do you think?

  16. Hi Faheem, it's possible. If you can post your code somewhere (skydrive, dropbox, etc.) I could take a look.

  17. smith says:

    Organized content is the best way to display or post an article, thank you for making it easy to digest your post.

    <a href="http://www.agpic.com">AGPIC</a&gt;

  18. Dave says:

    I'm seeing problems the BeforeSendReply if the web service method being called does NOT return anything (void return rather than a string as in your example).  The reply.IsEmpty is false and when it reaches the following like in the MessageToString method it throws an Unexpected End of FIle error (for the obvious reason that the MemoryStream has a length of zero):

    reader = XmlDictionaryReader.CreateTextReader(ms, XmlDictionaryReaderQuotas.Max);

    Is there something on the Message class that I can use to avoid calling MessageToString in this case?

  19. Barak Ulmann says:

    Hi Carlos, Thanks for the extensive and clear article.

    Is there a way to share custom context (correlation state) between request and reply formatters (generated by WebHttpBehavior.GetRequestDispatchFormatter and WebHttpBehavior.GetReplyDispatchFormatter respectively)? I want to alter the response JSON conversion based on request format (specifically enum value conversion). I did not find a way to pass that information from request formatter to response formatter.



  20. Barak Ulmann says:

    Figured it out using OperationContext.Current.RequestContext.RequestMessage.Properties

Skip to main content