ASP.NET August 14, 2010

Template for RESTish JSON service with WCF

Service contract, the service and a whole bunch of configuration. All code needed for a simple REST (sort of) 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.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

More about ASP.NET