Tuesday, November 20, 2007

Simple GET REST Service With WCF

The REST vs. WS-* debate has been pretty active lately.  I grew up in the SOAP school, and haven't spent much time in the REST world, so I thought I'd put together an example to learn a little more about putting together a REST service with the tools I use every day.

The concept around REST is that it's pretty much what you see is what you get.  URLs are used to identify an entity, and request methods define what it is you do with that entity.  GET is obviously to retrieve an entity, or a list of URLs for a collection of entities.  POST is to add a new entity, PUT is to update an entity, and DELETE is to remove an entity.

The sample I've provided is very simple, and assumes the GET method is used, and does nothing with the URL to determine the exact data item to retrieve.

The contract for the service is pretty simple an generic.  You can use it for every endpoint.

using System.ServiceModel.Channels;
using System.ServiceModel;
 
namespace RESTService
{
    [ServiceContract]
    public interface IRESTService
    {
        [OperationContract(Action = "*", ReplyAction = "*")]
        Message ProcessMessage(Message input);
    }
}

The service implementation for my contacts service looks like below.  Here is where I would need to put more logic to handle different request methods and some URL parsing to discover exactly what contact the request is for (i.e., http://localhost:50344/contact.svc/contacts/1234/)

using System.ServiceModel.Channels;
using System.Net;
 
namespace RESTService
{
    public class ContactService : IRESTService
    {
        public Message ProcessMessage(Message request)
        {
            Contact myContact = new Contact();
            myContact.FirstName = "Jim";
            myContact.LastName = "Fiorato";
 
            HttpResponseMessageProperty responseProperties = new HttpResponseMessageProperty();
 
            responseProperties.StatusCode = HttpStatusCode.OK;
            Message response = Message.CreateMessage(request.Version, request.Headers.Action, myContact);
 
            response.Properties[HttpResponseMessageProperty.Name] = responseProperties;
 
            return response;
        }
    }
}

My small Contact data structure is pretty simple:

using System.Runtime.Serialization;
 
[DataContract(Namespace = "http://tempuri.org/Contact")]
public class Contact
{
    [DataMember]
    public string FirstName;
 
    [DataMember]
    public string LastName;
}

And this portion of my web.config is of note as well:

<system.serviceModel>
    <bindings>
        <customBinding>
            <binding name="RESTBinding">
                <textMessageEncoding messageVersion="None" />
                <httpTransport />
            </binding>
        </customBinding>
    </bindings>
    <services>
        <service name="RESTService.ContactService">
            <host>
                <baseAddresses>
                    <add baseAddress="http://localhost:50344/" />
                </baseAddresses>
            </host>
            <endpoint address="contacts" binding="customBinding" bindingConfiguration="RESTBinding"
                contract="RESTService.IRESTService" />
        </service>
    </services>
</system.serviceModel>

So, when I visit my site with the URL http://localhost:50344/contact.svc/contacts, I'll get the following page.

Screen shot of REST service

Obviously, this is quite a bit more straight forward than the SOAP/WS-* alternative.  If I were to create this service with the SOAP tools in WCF, I'd get a page with a list of operations, and if I clicked on one of those, I'd get a test form if I was on the local server, to provide some post data to test my service.

I think it's thoughtful that WCF provides the support to do REST style services like this.  It's a bit more plumbing to write on my end.  As you can see from the service implementation, I've got to handle the request on my own.  But that request parsing facility should be fairly easy to abstract, write once and use often.

I'm attracted to this format of service delivery.  But before I hop on board, I need to do more investigation in to how a client would develop using this service with the tools that we've become accustomed to in the world of WSDL.  I need to think about not only the straight forward service references, but also the automatic JSON proxy we get from ASP.NET AJAX.