Thursday, May 27, 2010

Creating a RESTful web service using WCF and JSON

There are a vast number of technologies to choose from for implementing a web application. You can choose Flash, Silverlight, Java, HTML, JavaScript, CSS, ASP.NET, PHP, and a host of others. Putting aside arguments for or against such an approach, let's examine how we might create a RESTful web service in WCF that accepts and emits JSON.
First, we need to create a project. Create a new ASP.NET web application. We can delete the Default.aspx file as we will not be using it.
Next, let's add a WCF service to our project. When naming this service, keep in mind that this is your access point to the resource that is represented in our RESTful interface. If we are talking about Widgets, we might want to name it WidgetManager.svc. This will create three files for us:
  • IWidgetManager.cs - The interface describing service contract
  • WidgetManager.svc - The web service definition (markup)
  • WidgetManager.svc.cs - The web service implementation
First examine the code for our contract.
using System.ServiceModel; namespace RESTfulWCF { [ServiceContract] public interface IWidgetManager { [OperationContract] void DoWork(); } }
We need to indicate that this web service will be using JSON for communicating to the client. To do that we modify the service method description.
[OperationContract] [WebInvoke( ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)] void DoWork();
Notice that we need to add a reference to System.ServiceModel.Web to our project and add a using statement to include the definition of the WebInvoke attribute. The next step is to indicate which of the REST actions (GET, PUT, POST, DELETE) the method will correspond to, and what REST URL format is used to call this method. Let's say that our web service method is intended to retrieve a list of all of the widgets known to the system.
[OperationContract] [WebInvoke( Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "Widgets")] void GetWidgets();
That completes the definition of our web service contract. Assuming our site is hosted at the root of localhost, it is accessed using a GET request against the URL http://localhost/WidgetManager.svc/Widgets and will respond with JSON. But wait, our response type is void. Let's define a complex data type to represent the properties of our widget and return a collection of those widgets from our method.
using System.Runtime.Serialization; namespace RESTfulWCF { [DataContract] public class Widget { [DataMember] public string Name { get; set; } [DataMember] public int SprocketSize { get; set; } [DataMember] public int CogCount { get; set; } } }
And we update our return type to be a collection of this type.
[OperationContract] [WebInvoke( Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "Widgets")] Widget[] GetWidgets();
We need the function to return at least one item in our array to effectively demonstrate this code. Open the WidgetManager.svc.cs code behind file and implement the web service.
using System.Collections.Generic; namespace RESTfulWCF { public class WidgetManager : IWidgetManager { public Widget[] GetWidgets() { List widgets = new List(); widgets.Add( new Widget { CogCount = 3, Name = "Widget Alpha", SprocketSize = 6 } ); return widgets.ToArray(); } } }
We now need to tweak our web.config file so that the web server and the class description are in sync. We could define our service behavior in the web.config as well, but in this case it is easier to use a factory to set that up on our behalf. Open the WidgetManager.svc file markup and add the Factory.
<%@ ServiceHost Language="C#" Debug="true" Service="RESTfulWCF.WidgetManager" CodeBehind="WidgetManager.svc.cs" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
We still need to make a change to our web.config, but now we only need to define the endpoint behavior.
<system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="RESTfulWCF.WidgetManagerEndpointBehavior"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> <services> <service name="RESTfulWCF.WidgetManager"> <endpoint address="" binding="webHttpBinding" behaviorConfiguration="RESTfulWCF.WidgetManagerEndpointBehavior" contract="RESTfulWCF.IWidgetManager"> </endpoint> </service> </services> </system.serviceModel>
That's all we need to get started with this simple example. Compile the project and run. You will see a webpage that indicates the endpoint was not found. This is because the default start page is the service file, rather than the REST URL. Modify the URL to http://localhost/WidgetManager.svc/Widgets and you will get the JSON result emitted back.
[{"CogCount":3,"Name":"Widget Alpha","SprocketSize":6}]
The next thing we will want to do is accept a parameter in the request. To do this, create a new service method that accepts a parameter, in our case the ID of the widget to return.
[OperationContract] [WebInvoke( Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "Widgets/{widgetId}")] Widget GetWidget(string widgetId);
And remember to implement the method in our code behind file.
public Widget GetWidget(string widgetId) { return new Widget { CogCount = 3, Name = widgetId, SprocketSize = 6 }; }
Now when we run the project and browse to the URL http://localhost/WebService.svc/Widgets/117 we see the output widget carries the name we supplied in the URL.
An important note here is that the parameters passed in to the web service method in this way must all be strings. A UriTemplate may contain as many parameters as you like, and may appear anywhere in the URI. So a URI template of 'Widgets/{widgetId}/Cogs/{cogId}/Color' is completely acceptable.
This is just scratching the surface though, as we still haven't touched on the other three REST operations (PUT, POST, and DELETE). There are a number of options on the data contract attributes that allow you to modify the names of the JSON elements, as well as the order they appear in the JSON string. Source code for the example in this post is available here:

Friday, May 21, 2010

Indy Tech Fest 2010 Slides

Click the image above to download my slide deck for "Getting Started with Online Mapping Services" presented at Indy Tech Fest 2010.