Template for RESTish JSON service with WCF

I recently needed to create a simple service that exposed some data in JSON format with WCF. Since it turned out to be a bit trickier than I think it should be, much due to WCF’s extreme need for lots and lots of horrible XML configuration, and because I have a feeling that I’ll need to do something similar in the future I’m posting what I ended up doing for future reference. For more details about how to create a JSON service with WCF I strongly recommend reading Anders Tornblad’s post Creating a JSON-REST service using WCF. My code is largely based on his great series of posts with some differences when it comes to error handling and some configuration quirks (did I say that WCF is configuration heavy?) that I found necessary.

1. Classes to expose

The first thing we need is one or several classes that the service will expose in serialized form. As an example we could have a simple product class. The DataContract and DataMember attributes are optional but enables us to specify different names which is nice if we want to abide by Javascript conventions.

[DataContract(Name = "product")]
public class Product
{
    [DataMember(Name = "id")]
    public int Id { get; set; }

    //More properties...
}

2. A service contract

When we do File->New File in Visual Studio and add a WCF Service three files are added to our project: an interface with a ServiceContract attribute and two files (one for “markup” and one for code) for the actual implementation of the service.

[ServiceContract]
public interface IProducts
{
    [OperationContract]
    [WebGet(UriTemplate = "All", 
        ResponseFormat = WebMessageFormat.Json, 
        BodyStyle = WebMessageBodyStyle.Bare)]
    Product[] GetAllProducts();

    [OperationContract]
    [WebGet(UriTemplate = "ById/{id}", 
        ResponseFormat = WebMessageFormat.Json, 
        BodyStyle = WebMessageBodyStyle.Bare)]
    Product GetProductById(string id);
}

3. The service

The actual service implements the ServiceContract. In the below example both methods fetch data from a private property. In reality there would probably be some calls to a repository to retrieve domain objects and some mapping to the classes that are exposed through the service (I used AutoMapper to easily to that), either in the service or in an external class.

public class Products : IProducts
{
    public Pharmacy[] GetAllProducts()
    {
        return AllProducts.ToArray();
    }

    private IEnumerable<Product> AllProducts
    {
        get
        {
            //Retrieve and return all products
        }
    }

    public Pharmacy GetProductById(string id)
    {
        int idValue;
        if (!int.TryParse(id, out idValue))
        {
            throw new WebFaultException<string>(
                "Invalid id parameter. It must be an integer.", 
                HttpStatusCode.BadRequest);
        }
        var product = AllProducts.FirstOrDefault(p => p.Id == idValue);

        if (product != null)
            return product;

        throw new WebFaultException<string>(
            string.Format("Unable to find a product with id {0}", idValue), 
            HttpStatusCode.NotFound);
    }
}

4. Configuration

Finally there’s the configuration.

<services>
    <service name="Namespace.Products" 
        behaviorConfiguration="jsonRestDefault">
        <host>
            <baseAddresses>
                <add baseAddress=http://domain.com/productsservice/ />
            </baseAddresses>
        </host>
        <endpoint name="jsonRestEndpoint" behaviorConfiguration="RESTFriendly" 
            binding="webHttpBinding" contract="Namespace.IPharmacies">
        </endpoint>
        <endpoint contract="IMetadataExchange" 
            binding="mexHttpBinding" address="mex" />
    </service>
</services>
<behaviors>
    <serviceBehaviors>
        <behavior name="jsonRestDefault">
            <serviceMetadata httpGetEnabled="true" />
            <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
        <behavior name="RESTFriendly">
            <webHttp />
        </behavior>
    </endpointBehaviors>
</behaviors>
</system.serviceModel>

That’s a lot of XML! Anyhow, our service should now be reachable at http://domain.com/productsservice/Products.svc and the two methods can be reached at http://domain.com/productsservice/Products.svc/all and http://domain.com/productsservice/Products.svc/byid/123 where 123 is a product id.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Follow me on Twitter

  1. @chraas You might want to use it for complex parts of the site and not use it for simple rendering pages. 2 days ago
  2. @chraas In theory more SOLID, but I'm not sure it's worth the price. 2 days ago
  3. @chraas Be warned that you're introducing one big chunk of complexity with EPiMVP :) 2 days ago
follow me

Latest comments

  1. Berra S wrote "Read your post at http://joelabrahamsson.com/entry/using-xfo..." on PageData objects not returned as typed when using Page Type Builder and FindPagesWithCriteria
  2. Linus wrote "1 up for behaviour being as close as expected as possible!" on A common problem with Page Type Builder and UniqueValuePerLanguage set to false
  3. Joel Abrahamsson wrote "Hi Hans, Could it be that you previously didn't have Page..." on Page Type Builder 2.0 released

About this site

This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.

read more