Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

How to pass arbitrary data in a Message object using WCF

, 31 Mar 2009
Rate this:
Please Sign up or sign in to vote.
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:

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 @ http://blogs.msdn.com/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-web.aspx 

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 - http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.message.aspx

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:

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.

/// <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)

Share

About the Author

Leonid Sorokin
Software Developer
Canada Canada
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberladenedge28-Jul-11 20:05 
GeneralThank you! Pinmemberladenedge28-Jul-11 20:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 31 Mar 2009
Article Copyright 2009 by Leonid Sorokin
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid