Carlos' blog

Debugging tips and small examples for WCF

WCF "Raw" programming model (Web)

I've seen quite a few times in the forums people asking how to control exactly how the data returned by a WCF service. People want to use a certain format for the output of the data which isn't (natively) supported by WCF, such as XML or JSON. A few examples of questions of this nature:

  • I want to return the string "Hello world", but even though I set the BodyStyle property in the Web[Get/Invoke] attribute to Bare, the result is still wrapped in a <string> tag, like "<string xmlns="...">Hello world</string>; how do I remove the <string> wrapper?
  • I need to return my parameters in a certain format which is not supported by WCF. For example, my operation returns a list of triples, and I want them to be returned in a .csv-like format, with the header names in the first line and the values in subsequent lines. How can I get it done?

One way to do it is to create a separate MessageEncoder, which is capable of taking a Message object and converting it to the bytes, and use that encoder in the binding of the endpoint which contains the operation. Although this is certainly doable, it requires a lot of work - a Message object contains an XML representation of its contents, so you'd need a mapping between XML and whichever format you want, possibly requiring a new XmlWriter/XmlReader implementation; you'll also need all the plumbing parts needed to connect the encoder with the endpoint (a MessageEncoderFactory, a MessageEncodingBindingElement and so on).

The Web programming model introduced in WCF on .NET Framework 3.5 simplifies this task. The magic happens when the operation return type is of System.IO.Stream (the abstract class, not one of its concrete implementations). By returning a stream, WCF assumes that the operation wants total control over the bytes that will be returned in the response, and will apply no formatting whatsoever in the data that is returned. This service, for example, solves the first question listed above:

[ServiceContract]
public class RawService
{
    [OperationContract, WebGet]

    public System.IO.Stream GetValue()
    {
        string result = "Hello world";
        byte[] resultBytes = Encoding.UTF8.GetBytes(result);
        return new MemoryStream(resultBytes);
    }
}

This should work in most of the cases, since the result is fairly simple. It is a good practice, however, whenever you're using the raw programming model, to specify the Content-Type for the response. If nothing is specified, responses of operations with Stream return values will have a content type of application/octet-stream (i.e., binary). Some browsers (such as IE) may identify that the content is actually text, and print it correctly, but that's not a guarantee. The example will then become:

[ServiceContract]
public class RawService
{
    [
OperationContract, WebGet]
    public System.IO.Stream GetValue()
    {
        string result = "Hello world";
        byte[] resultBytes = Encoding.UTF8.GetBytes(result);
        WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
        return new MemoryStream(resultBytes);
    }
}

Notice that this isn't limited to text only; with the raw programming model you can create a service which returns pretty much anything, in any format, such as images created on the fly:

public class BlogPostRaw
{
    [
ServiceContract]
    public interface ITest
    {
        [
OperationContract, WebGet]
        Stream GetImage(int width, int height);
    }
    public class Service : ITest
    {
        public Stream GetImage(int width, int height)
        {
            Bitmap bitmap = new Bitmap(width, height);
            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    bitmap.SetPixel(i, j, (
Math.Abs(i - j) < 2) ? Color.Blue : Color.Yellow);
                }
            }
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, System.Drawing.Imaging.
ImageFormat.Jpeg);
            ms.Position = 0;
            WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
            return ms;
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(
typeof(ITest), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
        host.Open();
        Console.WriteLine("Host opened");
        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

When running the test method, you can point the browser to http://localhost:8000/Service/GetImage?width=50&height=40 to see that WCF can also be used as an image server :)

Published Thursday, April 17, 2008 9:30 PM by carlosfigueira
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Carlos' blog said:

The previous post mentioned how to return arbitrary data from WCF services. To receive data, however,

April 17, 2008 6:41 PM
 

sujeetsSpace said:

What the differnt ways you can dispose the Stream that is returned? One way is to dispose the stream when the service type is disposed. However that will only work for per call mode.

August 11, 2008 8:37 PM
 

richardcollette said:

Interesting.  The benefit that I see but not clearly stated here is that you are able to produce HTTP (and only Http since WebOperationContext is being used) protocol services that do not rely on IIS.   It is like an httpHandler except no dependency on IIS.

November 12, 2008 12:16 PM
 

dotnetCarpenter said:

I've been looking all over the Net for this! You are a god send!

October 9, 2009 12:55 PM

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required
Submit

About carlosfigueira

Software Design Engineer in Test at the Connected Frameworks team at Microsoft.

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker