How do I modify WCF to process messages in a different (non SOAP) format? How do I modify WCF to process messages in a different (non SOAP) format? xml xml

How do I modify WCF to process messages in a different (non SOAP) format?


Details of my implementation of Tim's answer

I needed to write this up for the client I am currently working for so I thought I might as well post it up here too. I hope it helps someone. I've created a sample client and service that I used to try out some of these ideas. I've cleaned it up and added it to github. You can download it here.

The following things needed to be implemented to allow WCF to be used in the way that I required:

  1. WCF to not expect a SOAP message
  2. The ability to format the request and response messages exactly as required
  3. All messages to be considered for processing
  4. Incoming messages to be routed to the correct operation to handle them

1. Configure WCF to not expect a SOAP message

The first step was getting the incoming message through the TextMessageEncoder. This was achieved by using a custom binding with the MessageVersion.None setting on the textMessageEncoding element.

  <customBinding>    <binding name="poxMessageBinding">      <textMessageEncoding messageVersion="None" />      <httpTransport />    </binding>  </customBinding>

2. Format the Message Correctly

The message formatter is required as the incoming message refused to be de-serialized by the existing XML formatter without the addition of additional attributes on the message contracts. This wouldn't normally be a problem but running my clients ebXML schemas through XSD.exe generates a 33000 line cs file and I didn't want to have to modify this in any way. Besides people would forget to re-add the attributes in future so this hopefully makes maintenance easier too.

The custom formatter expects to convert the incoming message to the type of the first parameter and to convert the return type into a response message. It inspects the implementing method to determine the types of the first parameter and return value in the constructor.

public SimpleXmlFormatter(OperationDescription operationDescription){    // Get the request message type    var parameters = operationDescription.SyncMethod.GetParameters();    if (parameters.Length != 1)        throw new InvalidDataContractException("The SimpleXmlFormatter will only work with a single parameter for an operation which is the type of the incoming message contract.");    _requestMessageType = parameters[0].ParameterType;    // Get the response message type    _responseMessageType = operationDescription.SyncMethod.ReturnType;}

It then simply uses the XmlSerializer to serialize and deserialize the data. An interesting part of this for me was the use of a custom BodyWriter to serialize the object into the Message object. Below is part of the implementation for the service serializer. The client implementation is the reverse.

public void DeserializeRequest(Message message, object[] parameters){    var serializer = new XmlSerializer(_requestMessageType);    parameters[0] = serializer.Deserialize(message.GetReaderAtBodyContents());}public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result){    return Message.CreateMessage(MessageVersion.None, _responseMessageType.Name,                                 new SerializingBodyWriter(_responseMessageType, result));}private class SerializingBodyWriter : BodyWriter{    private readonly Type _typeToSerialize;    private readonly object _objectToEncode;    public SerializingBodyWriter(Type typeToSerialize, object objectToEncode) : base(false)    {        _typeToSerialize = typeToSerialize;        _objectToEncode = objectToEncode;    }    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)    {        writer.WriteStartDocument();        var serializer = new XmlSerializer(_typeToSerialize);        serializer.Serialize(writer, _objectToEncode);        writer.WriteEndDocument();    }}

3. Process All Incoming Messages

In order to instruct WCF to allow all incoming messages to be processed I needed to set the ContractFilter property on the endpointDispatcher to an instance of the MatchAllMessageFilter. Here is a snippet illustrating this from my endpoint behaviour configuration.

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher){    endpointDispatcher.ContractFilter = new MatchAllMessageFilter();    // Do more config ...}

4. Selecting The Correct Operation

This was achieved by creating a class that implements IDispatchOperationSelector. In the SelectOperation method I inspect the incoming message. Here I'm looking for two things: 1. A sanity check that the namespace of the root element is the same as the namespace of the service contract 2. The name of the root element (note the use of LocalName to remove any namespace prefix)

The name of the root element is the return value and that will map to any operation on the contract with a matching name or where the action attribute has a matching value.

public string SelectOperation(ref Message message){    var messageBuffer = message.CreateBufferedCopy(16384);    // Determine the name of the root node of the message    using (var copyMessage = messageBuffer.CreateMessage())    using (var reader = copyMessage.GetReaderAtBodyContents())    {        // Move to the first element        reader.MoveToContent();        if (reader.NamespaceURI != _namespace)            throw new InvalidOperationException("The namespace of the incoming message does not match the namespace of the endpoint contract.");        // The root element name is the operation name        var action = reader.LocalName;        // Reset the message for subsequent processing        message = messageBuffer.CreateMessage();        // Return the name of the action to execute        return action;    }}

Wrapping It All Up

To make it easier to deploy I created an endpoint behaviour to handle the configuration of the message formatter, contract filter and operation selector. I could also have created a binding to wrap up the custom binding configuration but I didn't think that part was too difficult to remember.

One interesting discovery for me was that the endpoint behaviour can set the messages formatter for all the operations in the endpoint to use a custom message formatter. This saves the need to configure these separately. I picked this up from one of the Microsoft samples.

Helpful Documentation Links

The best references I've found so far are the Service Station MSDN magazine articles (Google "site:msdn.microsoft.com service station WCF").

WCF Bindings in Depth - Very useful information on configuring bindings

Extending WCF with Custom Behaviours - The best source of information on dispatcher integration points that I have yet found and they contain some really useful diagrams that illustrate all the integration points and where they occur in the order of processing.

Microsoft WCF samples - There is a lot in here that isn't documented very well elsewhere. I found reading through the source code for some of these very instructive.


I don't think that you need to do anything with bindings. I'm assuming that you need to send the ebXML formatted message over HTTP anyway?

@ladislav's answer is one approach, but I think message encoders are designed to work at a much lower level than what you're trying to achieve. They're essentially the pieces that encode the messages to and from the underlying stream (i.e., how the message is represented as bytes on the stream).

I think what you need to do is implement a custom Message Formatter. In particular, since you say that you want to submit the messages to a third-party, then I think it's only the IClientMessageFormatter interface that you'll need to implement. The other interface (IDispatchMessageFormatter) is used on the server side.

You'll also need to implement an appropriate ServiceBehavior and OperationBehavior to install the formatter into the stack, but the code for this will be minimal (the bulk of the code will be in implementing the above mentioned interface).

Once implemented, you could use the "one method to process them all" approach to test and debug your formatter. Simply take the received message and dump it out to the console for you to review, and then also send an ebXML response back. You could also use the same approach to build up your unit-testing.


For custom message format you need Custom MessageEncoder. MSDN contains example how to create custom encoder. If you use Reflector you will find several encoder implementations so you can learn how to write it.

You can also check what will happen if you try to use TextMessageEncoder with MessageVersion.None (I have never tryed it).