More on RESTful Service with WCF and POX/POCO

Kirk Eveans wrote a blog post about Creating RESTful Services Using WCF, which gives you a good understanding of how to get started with REST on WCF. In his sample, Kirk has 2 methods in a REST interface:

[ServiceContract]
public interface IService
{
    [OperationContract]
    [WebGet(UriTemplate = "customers/{id}")]
    Customer GetCustomer(string id);

    [OperationContract]
    [WebInvoke(UriTemplate = "customers")]
    Customer PostCustomer(Customer c);
}

The Data Contract for Customer looks like this:

[DataContract(Namespace = "")]
public class Customer
{
    [DataMember]
    public string ID { get; set; }
    [DataMember]
    public string Name { get; set; }
}

Kirk also describes how to use the Fiddler tool to send REST request to the service, which is a wonderful tool for these circumstances.

Now, to get the customer with ID 123, just send a GET request to the url: http://127.0.0.1:8000/customers/123 and the service will return:

<Customer>
<ID>123</ID>
<Name>Demo User</Name>
</Customer>

To call the other method, PostCustomer(), send a POST request to http://127.0.0.1/customers with the following request body:

<Customer>
<ID>123</ID>
<Name>Demo User</Name>
</Customer>

This returns:

<Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ID>123</ID>
<Name>Hello, Demo User</Name>
</Customer>

NOTE: Remember that you must add a HTTP header, specifying the content type, or the POST request will fail (Content-Type: application/xml).

Wrong Order of Nodes?

But what if the programmer sends the <Name> node before the <ID> node? Like this:

<Customer>
<Name>Demo User</Name>
<ID>123</ID>
</Customer>

The service will then return:

<Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ID i:nil="true"/>
<Name>Hello, Demo User</Name>
</Customer>

Note that the ID is null! If the ID was declared as an integer in the Data Contract, the response from the service would be:

<Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ID>0</ID>
<Name>Hello, Demo User</Name>
</Customer>

Note that ID is 0 (zero), which could become a somewhat hard bug to catch. I asked Kirk about this and he confirmed the reason for this behaviour is the way the DataContractSerializer works. If no order is specified in the DataContract, it will (de)serialize in alphabetic order. If this is a problem for you, there is away around it by specifying the XmlSerializerFormat attribute on the REST interface:

[ServiceContract]
public interface IService
{
    [OperationContract]
    [WebGet(UriTemplate = "customers/{id}")]
    [XmlSerializerFormat]
    Customer GetCustomer(string id);

    [OperationContract]
    [WebInvoke(UriTemplate = "customers")]
    [XmlSerializerFormat]
    Customer PostCustomer(Customer c);
}

POCO Support in SP1

One of the new features in .NET 3.5 SP1 is the support for POCOs - the DataContractSerializer supports serializing types that doesn't have the [DataContract] or [Serializable] attributes. Aaron Skonnard has a good post on this. This means you can safely get rid of the attributes on the Customer class:

public class Customer
{
    public string ID { get; set; }
    public string Name { get; set; }
}

Note that the DataContractSerializer is still picky about the XML it gets to be able to deserialize it properly. Again, to get a more "relaxed" REST interface where WCF accepts the Name and ID nodes in any order, use the XmlSerializerFormat. I'm not sure this is what you want, but it's an option.

I had a short mail conversation with Kirk about this, and he raised an interesting question about the lack of a industry accepted standard for describing RESTful services and I think he's right there. The XML-node order wouldn't be a problem at all if I gave the client programmer a schema or a contract which specified exactly how the RESTful interface was to be accessed and in which order the XML-nodes must come. Kirk had a lot to say about this, and I do hope he writes up a blog post about his thoughts ;)

There are ways to send and receive any XML to a RESTful interface with WCF, and I'll write a blog post about that another day.

No Comments