How to Use the WCF Web API to Build REST-Style Services for Windows Azure Websites

How to Use the WCF Web API to Build REST-Style Services for Windows Azure Websites

Use the WCF Web API within your ASP.NET MVC 3 web roles on Windows Azure

If you are building Windows Azure websites, chances are you've had to build supporting web services to provide data to those front ends, typically accessed thru Ajax or jQuery. In this article, we'll examine the preview 5 version of the WCF Web API, available for free on CodePlex, which gives you a first-class experience for building REST-style services to support your website UIs. I'll show you how to leverage the WCF Web API from within a Windows Azure ASP.NET MVC 3 web role.

Getting Started

The best way to approach the WCF Web API is to get your hands dirty! In this sample, we will build a variant of the simple contact manager application included with the WCF Web API samples. I will show how to get the WCF Web API up and running in a web role, then explore some of the API's features. To get started, make sure you have the Windows Azure SDK August 2011 release installed with Visual Studio 2010, as well as the Fiddler web debugging proxy and the WCF Web API. The easiest way to install the WCF Web API is to use NuGet, which you will want to install before continuing. You can download NuGet directly from nuget.codeplex.com.

Creating the Project

One of the new cloud project types introduced in the August release of the Azure SDK is ASP.NET MVC 3 Web Role. We begin by creating a new Windows Azure project and adding to it the .NET 4 role ASP.NET MVC 3 Web Role, as shown in Figure 1.

Figure 1: Creating an ASP.NET MVC 3 web role
Figure 1: Creating an ASP.NET MVC 3 web role

In the New ASP.NET MVC 3 Project dialog, shown in Figure 2, simply choose an Empty project and click OK.

Figure 2: Adding an empty MVC project to the web role
Figure 2: Adding an empty MVC project to the web role

Adding the Web API to the Project

To download and add the Web API, just right-click MvcWebRole1, then select Add Library Package Reference. In the dialog, click the Online tab at the left, and in the search box enter webapi.all, as shown in Figure 3.

Figure 3: Finding the WCF Web API via NuGet
Figure 3: Finding the WCF Web API via NuGet

Incidentally, if you type just "webapi", you'll see there's already a small community of contributions built around the WCF Web API. The WebApi.all package includes the HttpClient, JsonValue, WebApi.Core, WebApi.Enhancements, and WebApi.dll packages. Next, click the Install button, then review and accept the license.

After the process is completed (the WebApi.dll entry in the dialog will contain a green checkmark-see Figure 4), click Close on the Add Library Package Reference dialog. You now have several references to WebApi assemblies added to your MVC 3 Web Role project.

Figure 4: Message showing that the WCF Web API is installed
Figure 4: Message showing that the WCF Web API is installed

You will find that, conveniently, all the newly added assemblies have been set with a CopyLocal value of true, so that the DLLs will be deployed with your Windows Azure project (these assemblies are not included in the current Windows Azure OS).

Creating the Service Class

Now, let us turn to creating the create, read, update, and delete (CRUD) service for managing contacts. We add a folder named APIs to the MVC 3 Web Role project and within that folder add a class named ContactsApi. Decorate the ContactsApi class with the ServiceContract attribute (you will need to add a using statement for System.ServiceModel).

The next step is to create an MVC route for the service, so it is accessible at api/contacts. This is accomplished by using the MapServiceRoute extension method included with the WCF Web API. Within Global.asax.cs, we add a using statement for Microsoft.ApplicationServer.Http.Activation and one for the service type (MvcWebRole1.APIs). Within the RegisterRoutes method, we add a call to MapServiceRoute(), as shown in Figure 5.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
  var config = new Microsoft.ApplicationServer.Http.WebApiConfiguration() 
       { EnableTestClient = true };
  routes.MapServiceRoute("api/contacts", config);
 
 routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );
}

Creating the Entity

Within the MVC 3 Web Role, we create a new folder called Resources that will hold all entity types used. Then we add a new class named Contact.cs within that folder and give the class an integer ContactId property and a string Name property, as shown in Figure 6.

namespace MvcWebRole1.Resources	
{
    public class Contact
    {
    public int ContactId { get; set; }
    public string Name { get; set; }
    }
}

Returning Entity Data

Let's now return to the ContactsApi class, to add a service operation that returns a collection of contact entities. First, add a using statement for the namespace containing the contact entity type. Next, add a method that returns a List of the contact type. Attribute that method with the WebGet attribute (you will have to add a using statement for System.ServiceModel.Web), and specify an empty string for the UriTemplate property, so that this method is the default response to HTTP GET requests targeting /api/contacts instead of /api/contacts/get, which is the default behavior. Figure 7 shows the completed result.

using System.ServiceModel;
using MvcWebRole1.Resources;
using System.ServiceModel.Web;
using Microsoft.ApplicationServer.Http.Dispatcher;
using System.Net;
 
namespace MvcWebRole1.APIs
{
    [ServiceContract]
    public class ContactsApi
    {
    static List contacts = new List()
        {
        new Contact {ContactId = 1, Name="Zoiner Tejada"},
        new Contact {ContactId = 2, Name="John Smith"}
        };
 
     [WebGet]
    public List Get()
    {
        return contacts;
    }
    }
}

OData URI-Based Querying

The WCF Web API enables you to query collections using the OData URI-based query strings-to do so, you just have to change your service operations to return IQueryable instead of List. In the example I've shown, you would change the signature to return IQueryable and use the AsQueryable extension method to return the list of contacts as an instance of IQueryable-as shown in Figure 8. This enables you to run OData queries with orderby, top, skip, or filter query-string options, such as the following:

[WebGet]
public IQueryable Get()
{
    return contacts.AsQueryable();
}

Running Locally

You can run the solution locally in the Compute Emulator; just make sure you browse to /api/contacts to invoke the Get operation of the Contacts service-for example, "http://127.0.0.1:81/api/contacts". What you will see is an XML serialization of the list, similar to that shown in Figure 9.

Figure 9: Sample  browser output list of contacts

Creating CUD Operations

Within the service, adding operations for creating, updating, and deleting (i.e., CUD) entities is straightforward when you represent each by using a separate HTTP verb (typically, Create maps to Post, Update maps to Put, and Delete maps to Delete) with the appropriate UriTemplate. Figure 10 shows a complete implementation.

[ServiceContract]
public class ContactsApi
{
    static List contacts = new List()
    {
        new Contact {ContactId = 1, Name="Zoiner Tejada"},
        new Contact {ContactId = 2, Name="John Smith"}
    };
 
 [WebGet]
    public List Get()
    {
    return contacts;
    }
 
 [WebGet(UriTemplate = "{contactId}")]
    public Contact GetContact(int contactId)
    {
    var contact =  contacts.Find(c => c.ContactId == contactId);
    if (contact == null)
        throw new HttpResponseException(HttpStatusCode.NotFound);
 
     return contact;
    }
 
 [WebInvoke(Method = "POST")]
    public Contact AddContact(Contact contact)
    {
    contact.ContactId = contacts.Count + 1;
    contacts.Add(contact);
    return contact;
    }
 
 [WebInvoke(UriTemplate = "{contactId}", Method="PUT")]
    public Contact UpdateContact(Contact contact, int contactId)
    {
    var originalContact = contacts.Find(c => c.ContactId == contactId);
    if (contact == null)
        throw new HttpResponseException(HttpStatusCode.NotFound);
    originalContact.Name = contact.Name;
    return originalContact;
    }
 
 [WebInvoke(UriTemplate = "{contactId}", Method = "DELETE")]
    public Contact DeleteContact( int contactId)
    {
    var contact = contacts.Find(c => c.ContactId == contactId);
    if (contact == null)
        throw new HttpResponseException(HttpStatusCode.NotFound);
 
     contacts.Remove(contact);
    return contact;
    }
} 

The WCF WebAPI supports conventions that can simplify your WebGet and WebInvoke declarations: You no longer have to specify the UriTemplate property for empty "" values (it is now the default), and if you prefix your operation name with an HTTP verb (e.g., PostContact), then you do not need to specify the HttpMethod property. In the code listing in Figure 10, notice the use of the HttpResponseException, provided as part of the HttpEnhancements of the WCF Web API, which is used to return 404 - Not Found when the requested contact does not exist in the collection.

Deploying to Windows Azure

Before we deploy, we have to fix up the web.config to disable some default settings thrown in by the MVC 3 Web Role project. In web.config, since we are not setting up a SQL Azure database as a back end for this site, comment out or remove the authentication, profile, membership, role manager, and sessionState elements, as well as the connectionString referenced by these. Next, deploy! You just deploy in the normal way by using the Publish… option, by right-clicking the Cloud Project. That is all there is to getting the WCF Web API into the cloud.

With the overview behind us, let's look at some of the interesting features of the API.

Server-Driven Content Negotiation

If your client includes the appropriate Accept header, you can leverage the WCF Web API's support for server-driven content negotiation. By default, responses are sent in XML, but you can switch to JSON by submitting requests with the following header:

Accept: application/json

This tells the API to use the JSON formatter that uses the DataContractSerializer to serialize the list using JSON. The body of the response will include the JSON serialized payload, similar to the following:

[{"ContactId":1,"Name":"Zoiner Tejada"},{"ContactId":2,"Name":"John Smith"}]

The encoders used are extensible, and the API provides mechanisms that enable you to plug in your own.

HttpRequestMessage and HttpResponseMessage

The WCF Web API introduces the HttpRequestMessage and HttpResponseMessage classes, both of which simplify interaction with the requests and responses, providing simple mechanisms to get and set both headers and the message body.

Working with raw requests and responses. Getting and setting the value of the response body (or building up a request) is simplified, owing to both the HttpRequestMessage and HttpResponseMessage types as well as the StringContent helper class that takes care of setting the Content-Type header with an encoding and specified media type (e.g., JSON, plain text, XML). You can set the value using one of the overloads of the StringContent constructor, but it defaults to Content-Type: text/plain; charset=iso-8859-1. For example, we can change the AddContact method we created previously to return an HttpResponseMessage with a text payload, as shown in Figure 11.

[WebInvoke(Method = "POST")]
public HttpResponseMessage AddContact(Contact contact)
{
    contact.ContactId = contacts.Count + 1;
    contacts.Add(contact);
    var response = new HttpResponseMessage();
    response.Content = new StringContent(string.Format("Added contact {0}:{1}", contact.ContactId, contact.Name));
    response.StatusCode = HttpStatusCode.Created;
    return response;
}

Working with headers. The WCF Web API makes it easier to interact with the lower-level details of the response and request, and this includes enabling simplified access to the common HTTP headers, such as ETag, Cache-Control, and Expires, which you commonly need to control the caching behavior of your responses.

To give you an example, to set a response as cacheable for 20 seconds, you previously had to rely on static methods that provided very abstracted influence over the headers, like this:

Response.Cache.SetExpires ( DateTime.Now.AddSeconds ( 20 ) ) ;
Response.Cache.SetCacheability ( HttpCacheability.Public ) ;

With the WCF Web API, things get a lot more explicit and direct. Now you do this within your resource operation:

var resp =  new HttpResponseMessage>(contacts);
resp.Content.Headers.Expires = DateTime.Now.AddSeconds(20);
resp.Headers.CacheControl = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(20), Public = true };
return resp;

Figure 12 shows the resultant response headers; observe the values of Cache-Control, Date, and Expires.

Figure 12: Resultant response headers
Figure 12: Resultant response headers

One of the enhancements included in the WCF Web API was ported over from Silverlight: the JsonValue class and related classes JsonObject, JsonPrimitive, and JsonArray. These types enable you to safely navigate object properties of a JSON string in a weakly typed fashion, using the dotted notation that is quite familiar to the JavaScript developer. In other words, you do not have to create the hierarchy of concrete classes to deserialize into. The WCF Web API also includes helper methods that help you in converting a query string (FormUrlEncodedExtensions.GetQueryStringAsJsonObject) or post body (FormUrlEncodedExtensions.ParseFormUrlEncoded) into a JsonObject instance.

Enhanced HTTP Client

In addition to all the server-side benefits, the WCF Web API also provides a new HTTP client. HttpClient provides straightforward methods for Get, Post, Put, and Delete that map to the corresponding HTTP verbs, in addition to a general-purpose Send method. All these methods return an HttpResponseMessage and may take either HttpContent (a la StringContent) or HttpRequestMessage as part of their input.

Closing Thoughts

Currently the WCF Web API is in a preview release. Although it is intended to replace the REST starter kit, and will ultimately find its way into .NET, the current package is not licensed for redistribution. However, it is free for use at your own risk on your own servers (or even Azure servers) running your product. In other words, go get it, get your hands dirty, and start building your REST-style services on Azure with the WCF Web API.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish