Positive Business Change

Agile Musings

I am currently writing an application that will be accessed via multiple front ends (website, iOS, Android, etc), so the core of the application is behind a RESTful Api. I had to jump through a few hoops whilst working out how to accomplish this using WCF 4 and wanted to share those in case anyone else is facing similar issues.

Comment unsuccessful. Please correct the errors below.

Passing Json data to a RESTful WCF 4 service using Json.Net

The key to working with Json data and WCF services is to tell the service that it is receiving data in Json format. As well as formatting your data as Json, you must also add the correct header to the outgoing HTTP request that you send to the service and ensure that your service expects to receive Json requests and reply with Json responses. In this article I will explain the approach that I took. The goal we are trying to achieve is to create a new Effect Map entity (more on this in a moment) and save this in a data store somewhere. I'm only focusing on the passing of data from client application to service so won't look at either the front end application or data store. Let's take a quick look at our EffectMap entity that lives server side and the client side EffectMapViewModel object that we translate into Json to send across to our service.

The EffectMap entity model

public abstract class Entity { [Key] public int EntityId { get; set; } } public class EffectMap : Entity { public string Title { get; set; } public int? MapGoalId { get; set; } [ForeignKey("MapGoalId")] public BusinessGoal MapGoal { get; set; } } public class BusinessGoal : Entity { public string Goal { get; set; } }

The EffectMapViewModel client class

public class EffectMapViewModel { [DisplayName("Give the map a title")] public string MapTitle { get; set; } [DisplayName("What's the business goal for the project?")] public string MapBusinessGoal { get; set; } }

The attributes can all be found in the System.ComponentModel and System.ComponentModel.DataAnnotations namespaces.

Creating the RESTful Api

The first step is to create the WCF REST project that will contain our services. Fortunately there is a Visual Studio extension that provides a REST Service Template, which we will install before we go any further. Access the Extension Manager from the Tools menu in Visual Studio, search for and install the "WCF REST Service Template 40(CS)". You may also notice a similar extension named "WCF REST Service with API Key". If you are thinking of creating a public REST API that will be accessed by multiple clients I would strongly suggest performing API Key verification to protect your service from malicious clients. I won't focus on that for this post but would direct the inquisitive reader to an article by Ron Jacobs that explains the principle: How to do API Key Verification for REST Services in .NET 4

Once our template is installed and we have created a new WCF REST Service Application, we can go ahead and create our simple REST service. You get a default service out of the box, which you can modify if you so choose, but for our purposes we're going to delete that and create our own. Add a new WCF Service called EffectMapService.cs. Now open up Global.asax.cs and change the default ServiceRoute to use our new service:

private static void RegisterRoutes() { RouteTable.Routes.Add(new ServiceRoute("effectmap", new WebServiceHostFactory(), typeof(EffectMapService))); }

This basically allows us to map a base URL for our service, in exactly the same way as MVC routing works - a very welcome WCF4 addition. We are going to use a POST method rather than a PUT method, because we may well have other properties on our entity (CreatedDate, LastModifiedDate for example), which will be different each time we make the service request. Here's a useful article on the difference between PUT and POST. Our POST method call is going to live at the URL /effectmap/create and looks like this:

[WebInvoke(UriTemplate = "/create", Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] public EffectMap CreateMap(EffectMap instance) { // pass entity across to data layer for saving ... return instance; }

A little Api client

Now that our service is up and running we can build a client class to be responsible for calling the service and passing Json data to and fro. Because we're conscientious about adhering to the single responsibility principle and loose coupling, we'll introduce a couple of dependent classes for performing some of the key functions. The first is a custom serializer that creates the Json string from our ViewModel object. It makes use of the Newtonsoft.Json.Linq library (part of Json.Net - search for Newtonsoft.Json on Nuget):

public interface IApiSerializer { string SerializeEffectMap(EffectMapViewModel source); } public class EffectMapApiSerializer : IApiSerializer { public string SerializeEffectMap(EffectMapViewModel source) { var effectMapJson = new JObject( new JProperty("EntityId", 0), new JProperty("MapGoal", new JObject( new JProperty("EntityId", 0), new JProperty("Goal", source.MapBusinessGoal) )), new JProperty("MapGoalId", 0), new JProperty("Title", source.MapTitle) ); return effectMapJson.ToString(); } }

Next up is a factory class for creating some of the Api objects we will be interacting with. The second of these, JsonContent, will contain the content that we pass to the HttpClient post method call. More on that in a moment. First, the ApiObjectFactory:

public interface IApiObjectFactory { HttpClient CreateHttpClient(Uri baseAddress); JsonContent CreateJsonContent(string json); } public class ApiObjectFactory : IApiObjectFactory { public HttpClient CreateHttpClient(Uri baseAddress) { return new HttpClient { BaseAddress = baseAddress }; } public JsonContent CreateJsonContent(string json) { return new JsonContent(json); } }

I'm in two minds as to whether our ApiObjectFactory breaks the Single Responsibility Principle. On the one hand, the class is responsible for creating objects used by our Api Client. On the other, the objects are relatively unrelated. However, the complexity of the factory is very low, so we'll leave it as it is. Here's our JsonContent class:

public class JsonContent : StringContent { public JsonContent(string content) : this(content, Encoding.UTF8) { } private JsonContent(string content, Encoding encoding, string mediaType = "application/json") : base(content, encoding, mediaType) { } }

It is simply a wrapper for StringContent (derived from HttpContent) that sets the encoding and media type so that we don't have to explicitly set them each time we use it. The Post method of the HttpClient accepts an object of type HttpContent. Finally we will create a wrapper class for our HttpClient that simply calls the underlying post method:

public interface IHttpClientWrapper { HttpResponseMessage Post(HttpClient client, string requestUri, HttpContent content); } public class HttpClientWrapper : IHttpClientWrapper { public HttpResponseMessage Post(HttpClient client, string requestUri, HttpContent content) { var task = client.PostAsync(requestUri, content); return task.Result; } }

Now that we have our dependent classes, let's look at the ApiClient itself:

public class EffectMapApiClient { private readonly IApiSerializer apiSerializer; private readonly IApiObjectFactory apiObjectFactory; private readonly IHttpClientWrapper httpClientWrapper; private readonly Uri baseAddress; public EffectMapApiClient(string baseAddress) : base(new EffectMapApiSerializer(), new ApiObjectFactory(), new HttpClientWrapper(), baseAddress) { } public EffectMapApiClient(IApiSerializer apiSerializer, IApiObjectFactory apiObjectFactory, IHttpClientWrapper httpClientWrapper, string baseAddress) { this.apiSerializer = apiSerializer; this.apiObjectFactory = apiObjectFactory; this.httpClientWrapper = httpClientWrapper; this.baseAddress = new Uri(baseAddress); } public void CreateMap(EffectMapViewModel effectMap) { using (var client = this.apiObjectFactory. CreateHttpClient(this.baseAddress)) { var json = this.apiSerializer.SerializeEffectMap(effectMap); var content = this.apiObjectFactory.CreateJsonContent(json); var response = this.httpClientWrapper.Post(client, "create", content); if (response.StatusCode == HttpStatusCode.OK) { // do something positive } } } }

The last step is to check that the response comes back with a StatusCode of 200 so we know there weren't any errors during the trip to the service and we can then deserialize and use the response from the service in our client code.

Posted by: Simon Rigby
Last revised: 14 Jan, 2012 06:52 PM


No comments yet. Be the first!

Your Comments

Comment unsuccessful. Please correct the errors below.
Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.


Check me out on LinkedIn: