Mono WCF Advent Day 9: RESTful WebHttpBinding

| 6 Comments | No TrackBacks

REST support has been introduced in .NET 3.5. It includes JSON support for message serialization, and support for RSS and Atom for consuming web feeds (as serialization contract). It is implemented as WebHttpBinding in System.ServiceModel.Web.dll.

Unlike some of the topics I wrote earlier, our REST support compatibility for .NET 3.5 is almost feature complete (yes I prefer RESTians than SOAPy people).

URI Templates

REST services have "resources" identified by URLs, like: http://localhost/ShoppingService/Products/by-name/RupertsDanceLesson_DVD

While HTTP transport stack is reusable, the existing BasicHttpBinding does not fill such needs - the destination of its request is identified by SOAP request header or HTTP request content body. A dedicated message encoder is required.

The alternative mechanism used for processing such requests in WebHttpBinding is "UriTemplate" which is a syntax based on URI but includes expressions like '{x}'. For example: "/ShoppingService/Products/by-name/{name}"

When a client is to send a request with certain set of parameters, the client first determines the request URI by filling dynamic parts of the URI template. Then the client sends the request to the service. When the service received a request, it tries to determine which operation in the service should handle it, and extract parameter values from the actual request URI. There won't be files at the corresponding destination URI. It's all virtual.

JSON reader, writer, serializer

Requests in WebHttpBinding are created as an URI. Responses in REST services are often returned as JSON. With WebHttpBinding, you can read and write JSON in certain structure - since WCF Messages are serialized as "XML", which strictly means, XmlDictionaryReader/Writer, the JSON input and output are also represented as the XML reader and writer. I don't explain the details, but you can start reading from JsonReaderWriterFactory.

There also is another specialized type for JSON serialization i.e. DataContractJsonSerializer. It reads and writes JSON instead of XML.

JSON serializer could be also used for serializing complex request parameters.

Of course, WebHttpBinding does not always use JSON. Rather, it uses POX (plain old xml) by default.

RSS and Atom serialization

Another typical practice in REST world is use of syndication feeds such as RSS and Atom. WCF 3.5 added support for serialization of those feeds. In System.ServiceModel.Syndication namespace there is a couple of types for data contract serialization of feeds and feed items.

Operations using WebGet and WebInvoke

With WebHttpBinding the existing OperationContractAttribute is not sufficient to provide how to host services. A URI template must be associated to each operation. WebGetAttribute or WebInvokeAttribute are used onto an operation method to bind URI template. Though, you don't have to explicitly design the service URI; it will be automatically inferred like: http://...[baseuri]/[operation]?argFoo={argFoo}&argBar={argBar}...

Adding [WebGet] or [WebInvoke] does not automatically work for WCF engine to assure that it is RESTful service. You also have to use either a dedicated channel factory or a service host, or WebHttpBehavior, a custom IEndpointBehavior. While WebHttpBehavior plays the primary role for everything I explained above and more, former approach would be easier.

Now we are ready to write sample code and run.

// client
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Web;

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  [WebGet (RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json)]
  string Greet (string input);
}

public class Test
{
  public static void Main (string [] args)
  {
    string name = args.Length > 0 ? args [0] : "anonymous joe";
    string url = "http://localhost:8080";
    WebHttpBinding b = new WebHttpBinding ();
    var f = new WebChannelFactory (b, new Uri (url));
    IMyService s = f.CreateChannel ();
    Console.WriteLine (s.Greet (name));
  }
}

// service
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  [WebGet (RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json)]
  string Greet (string input);
}

public class MyService : IMyService
{
  public string Greet (string input)
  {
    return "hola, " + input;
  }
}

public class Test
{
  public static void Main ()
  {
    string url = "http://localhost:8080/";
    WebHttpBinding b = new WebHttpBinding ();
    var host = new WebServiceHost (typeof (MyService), new Uri (url));
    host.AddServiceEndpoint (typeof (IMyService), b, "");
    host.Open ();
    Console.WriteLine ("--- type [CR] to quit ---");
    Console.ReadLine ();
    host.Close ();
  }
}

In the example above, I used WebChannelFactory<T> instead of client class which is derived from ClientBase<T>, but as long as you use a gives instance of IMyService from it, its role is almost the same as ClientBase.

Limitations

Sadly WebHttpBinding is not available in Moonlight (nor Silverlight). It is not because the REST style does not fit Silverlight or RIA land in general, but because Silverlight API lacks behavioral extensibility API. I see there is no reason to not add things like IEndpointBehavior, but that's what Microsoft API does.

While WebHttpBinding is more WWW-friendly, it is not always possible to handle RESTful services. To handle JSON objects, it automatically adds type IDs during serialization and expects the IDs on deserialization.

No TrackBacks

TrackBack URL: http://veritas-vos-liberabit.com/monogatari/mt-tb.cgi/92

6 Comments

Hi Atshushi,

You said before: "Though, as a HTTP server, xsp outperforms HttpListener a lot, so it may work nicer in the future (unless you hit WCF-specific bugs). The simpler (especially for debugging) is those without xsp. I have to admit that ASP.NET integration is somewhat buggy so far."

Is this true also for WebHttpBinding? is WebHttpBinding using HttpListener? In my company we are gonna start to develop our new architecture and we are considering Mono and REST for it, but if you think that it's not gonna scale good... we'll wait to test with Mono :D

Thanks.

Yes and it depends. ASP.NET integration is not based on HttpListener; HttpListener is used only outside ASP.NET.

Though right now the REST stack at service side is pretty much unstable in trunk, so I wouldn't recommend to try it now. The HTTP channel stack has been under lots of bugfixes that wait for more integration usecase tests such as the REST stack.

Sorry if I misunderstand you, but can I get from your first sentence that if I use WCF REST in Apache/XSP the integration is better??

Ok, it seems like we'll start the development with .NET and I'll take a look on the Mono WCF progress regularly. I'm not a C/C++ programmer, but I could help with my C# knowlegde to testing or something, I'll be glad to do it.

Cheers.

Well, you had two questions, so I gave an answer for each ;)
When you host your service outside ASP.NET (i.e. ServiceHost), it does use HttpListener. Otherwise (inside ASP.NET), it doesn't. And as I answered yes to the first question, ASP.NET integration is buggier.

WCF implementation is fully C# land, so you might be able to dive into our sources:
http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/System.ServiceModel

HTH

That's really cool, seriously.

Have you considered support the WCF REST starter kit? I'm looking forward for the day I could run my REST services in apache and linux!!

Regards!

Yes, I had a look at REST starter kit earlier this year.
http://mono-project.com/WCF_Development_2010#REST_Starter_Kit_.28Microsoft.ServiceModel.Web.dll.29

Current situation is that 1) it is released as a pre-release component that is not opensourced and 2) we need some contributors to hack it in case it goes proprietary.

Leave a comment