Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

How to Pass Arbitrary Data in a Message Object using WCF

5.00/5 (1 vote)
31 Mar 2009Ms-PL2 min read 37.3K  
How to pass arbitrary data in a message object using WCF

I was searching all over the Internet, and yet I could not find any information about this seemingly simple task. Suppose you have an arbitrary string that you would like to return from a WCF (Windows Communication Foundation) service.

You can do one of the following on the service contract:

  1. Create a method/operation with a string return type
  2. Create a method/operation with a Stream return type in order to have full control over how serialization is performed. You can find a good explanation @ this link.

However, you may find yourself in a situation where you cannot return a Stream type because you are sending the response somewhere deep inside the WCF stack and you only have the System.ServiceModel.Message type to work with. Here is the documentation of the Message class I am talking about.

Aside: If you have ever worked with RIA (Rich Internet Applications) such as Flash or Silverlight, you will most likely be familiar with cross-domain policy files, such as crossdomain.xml (Flash) or clientaccesspolicy.xml (Silverlight). I am not going to go into why they are needed, but needless to say, I had to return one of these files depending on the request made from the client. Both of these files contain perfectly valid XML.

If you look carefully at the Message.CreateMessage method, you will see it has many overloads. At first, I tried this:

C#
Message reply = Message.CreateMessage(MessageVersion.None, "", 
XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes("[XML goes here]"))));

This works fine as long as the XML you are passing to XmlReader does not contain DTD definitions. For some security reason, having a DTD in the XML causes an exception on the XmlReader.Create call. I was able to avoid an exception by setting the ProhibitDtd property on XmlReaderSettings to false, but WCF decided I was performing evil deeds, and independently decided to emit my XML without the DTD definition.

If you look at the overloads of Message.CreateMessage, you will quickly realize that you must serialize as XML. In my case, the DTD in the crossdomain.xml caused the headache, but what if you want to pass an arbitrary string such as "Hello World" back from the service as-is, what do you do then? Anything you may try will cause the default DataContractSerializer to kick in and serialize your object. The catch is, you do not actually want serialization.

Here is the code that you need in order to return arbitrary text using WCF's Message class.

C#
/// <summary>
/// Necessary to write out the contents as text (used with the Raw return type)
/// </summary>
public class TextBodyWriter : BodyWriter
{
    byte[] messageBytes; 

    public TextBodyWriter(string message)
        : base(true)
    {
        this.messageBytes = Encoding.UTF8.GetBytes(message);
    } 

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("Binary");
        writer.WriteBase64(this.messageBytes, 0, this.messageBytes.Length);
        writer.WriteEndElement();
    }
} 

// .... 

// Here is sample code that does the magic of returning raw text to the client...
string response = "Hello World!";
Message reply = Message.CreateMessage(MessageVersion.None, null, 
    new TextBodyWriter(response));
// very important - forces WCF to serialize the message body contents as
// "Raw" which effectively means no serialization
reply.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(
    WebContentFormat.Raw); 
// can also be "application/xml" or anything you prefer 
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)