WCF has supported HTTP and RESTful approaches to service design for some time now, and while there are some exciting new enhancements being worked on as part of the WCF Web API project, there is some pretty rich functionality in WCF 4.0 that is definitely worth taking advantage of if you need to build HTTP/REST services today, especially when hosting in Windows Server AppFabric.
There a number of differences to consider when working with REST services that require some special attention as compared to traditional SOAP services, and those differences are definitely noticeable when working with WCF. In this post, I’d like to highlight some of the most common capabilities that REST developers ask for and how to accomplish them in general and when hosting in IIS or Windows Server AppFabric. The WCF Web API improves upon some of the techniques I cover here and introduces new ones which I’ll be discussing in future posts.
The first thing you want to do when working with WCF REST is pick a project template. While WCF 4 supports REST out-of-the-box (including a number of improvements over previous versions and the alignment of out-of-band features first seen in the WCF Starter Kit), the default WCF templates are really designed for SOAP-based services and thus are rather lacking for supporting REST an an intuitive, F5 manner (again, this will get better).
Microsoft provides the WCF REST Service Application template via the Visual Studio 2010 Extension Manager. In addition to some useful scaffolding for providing an F5 experience, some fundamental code and configuration is provided as part of the template which is necessary for working with a host such as IIS or Windows Server AppFabric. Ron Jacobs also has a great template specifically designed for Server AppFabric that extends the functionality in this template to support end to end monitoring and provides a web-based test harness among other things. I definitely recommend that you check it out as well.
To keep things focused on the basic aspects of WCF REST 4.0, I’ve taken the WCF REST Service Application template with very minor modifications and created a simple read-only GET service to illustrate some of the most common things you’re likely to hit when starting out with REST in WCF 4.
Out of the box, the WCF REST Service Application template provides some basic scaffolding including a functional read-only (GET) REST service. While “SampleItem” is certainly not the most interesting domain, I am going to leave the sample unaltered for simplicity sake as we are going to focus more on the hosting aspects of WCF REST than the design and the code.
Once you create a project with the WCF REST Service Application template, there are 4 files that are created in the project:
- The Service1.cs file includes the definition and implementation of the service.
- The SampleItem.cs file includes the Data Contract that models a resource.
- The Global.asax file defines some key events that we’ll hook into to enable dynamic routing.
- The Web.config file includes some key configuration for enabling dynamic routing and supporting extension-less (svc-less) URIs among other things.
The first thing you’ll probably notice in the template is that its missing an .svc file. Most REST developers would rather work with clean URIs as opposed to having a file extension get in the way. Fortunately, with a little bit of help from ASP.NET, we can leverage some pretty sophisticated routing capabilities that not only allow us to eliminate the need for the .svc extension, but in addition enable us to factor our URIs across a number of services and have some pretty fine grained control over URI routes using a templated approach.
If you hit F5 at this point, the service will run in Cassini, generating a rather glum response (note that you may not see XML immediately depending on the browser you are using; a brute force approach is to view source or, in the case of IE9, which I am using here, leverage the browser targeting capabilities provided by the F12 developer tools):
If you take a look inside the Global.asax.cs files, you’ll see what makes this magic happen:
1: RouteTable.Routes.Add(new ServiceRoute("Service1", new WebServiceHostFactory(), typeof(Service1)));
2:
As shown above, the template automatically added an entry to the System.Web.Routing.RouteTable. The Add method takes a ServiceRoute instance for which we provide 3 parameters:
- The first parameter is known as a route prefix and signals the root path that will be mapped to a given WCF service type. In this case, the URI segment of “Service1” will route to the Service1 class as specified in the third parameter.
- The second parameter is also interesting. Unlike traditional SOAP services that use a ServiceHost, the WebServiceHostFactory was designed to deal with HTTP-based services exclusively.
This default code can immediately be improved upon in my opinion by replacing the route prefix with a simple empty string. This will invoke the GetCollection method in the service implementation which provides a nice, intuitive user experience when interacting with the service without knowing much about it.
Taking a look at the service class and the GetCollection method, you will see some familiar attributes, along with the AspNetCompatabilityRequirements attribute which, among other things enables support for routing:
1: [ServiceContract]
2: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
3: [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
4: public class Service1
5: {
6: [WebGet(UriTemplate = "")]
7: public List<SampleItem> GetCollection()
8: {
9: // TODO: Replace the current implementation to return a collection of SampleItem instances
10: return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } };
11: }
12: }
All of this also requires that ASP.NET compatibility is enabled declaratively in the Web.config by setting the aspNetCompatibilityEnabled attribute to true in the serviceHostingEnvironment element:
1: <system.serviceModel>
2: <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
3: <standardEndpoints>
4: <webHttpEndpoint>
5: <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
6: </webHttpEndpoint>
7: </standardEndpoints>
8: </system.serviceModel>
You will also notice that the web.config is absent of the boilerplate service and endpoint configuration including the address, binding and contract attributes. WCF 4 introduces the concept of default endpoints to simplify the approach for hosting services in WCF 4, which in effect automates the creation of common endpoint configurations for getting your WCF services up and running quickly and easily. Standard Endpoints build on this simplicity by providing common configurations packaged into an element and exercising convention over configuration. By using the WebHttpEndpoint standard endpoint, you are provided some common configuration knobs such as supporting multiple encoding types and help documentation that is automatically generated provided you enable the helpEnabled attribute in line 5 above:
While all of this works just fine in Cassini, it won’t work in IIS/Windows Server AppFabric as without the additional configuration I am going to cover next because it (rightfully) assumes that you are asking to browse the contents of the root directory which is fortunately forbidden unless you explicitly allow it:
Note that had I left the route prefix with the default “Service1”, a 404 would instead be thrown because without the correct modules, IIS has no way of routing this to the class by the same name.
The remedy to these problems is provided by setting the runAllManagedModulesForAllRequests attribute in the modules element to true, which ensures that any requests that come into the request pipeline that are not mapped to an extension are passed on to the modules in the pipeline for processing. Specifically, the route that was added to the Application_Start method implementation in the Global class will be used to resolve to the Service1 class by the UrlRoutingModule managed module which is registered using the configuration below and enables this routing for services that do not use the .svc extension and are hosted in IIS/Windows Server AppFabric:
1: <system.webServer>
2: <modules runAllManagedModulesForAllRequests="true">
3: <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
4: </modules>
5: </system.webServer>
It is possible, depending on your IIS configuration that the UrlRoutingModule has already been registered, in which case line 3 above is superfluous. If you comment it out and re-deploy, things will continue to work as expected unless you either remove it declaratively in the web.config or explicitly remove it from the Modules manager.
With this configuration in place, the service will result in the request being correctly routed and the response presented in the browser:
If you’re like me, you likely find the XML, or at least the root node abhorrent. We can improve upon this significantly by creating a second class to represent the SampleItem that inherits from the original List<SampleItem> found in the template and decorate it with a CollectionDataContract attribute.
1: [CollectionDataContract(Name = "SampleItems",Namespace = "")]
2: public class SampleItems : List<SampleItem>
3: {
4:
5: }
Further, if we override the Namespace by setting the Namespace parameter of the attribute and setting it to an empty string, we get a much nice, cleaner result:
Note: I have to credit this technique to Jon Flanders, who’s excellent book “Restful .NET” is where I first learned this technique a couple of years ago.
Some developers and administrators may be concerned about the potential performance implications that enabling the runAllManagedModulesForAllRequests attribute introduces for static requests such as HTML pages, image files, etc. since each of these requests must run through all of the configured modules unnecessarily. Fortunately, Microsoft introduced an alternative to this approach in a QFE Hotfix which enables dynamic routing without impacting static requests. You can download and install the Hotfix from here: http://support.microsoft.com/kb/980368. After installing the hotfix, three new handlers with a name prefix of ExtnesionlessUrlHandler are installed in IIS which correspond to 32, 64 bit and integrated modes and applied to all applications. With these handlers installed, you can remove or disable the runAllManagedModulesForAllRequests attribute and all routing will continue to function as expected provided the handlers remain registered as shown below.
As I was walking through the standard endpoint configuration, you may have noticed an attribute on the standard endpoint element called automaticFormattingEnabled. When set to true, this allows you to manipulate whether the service should return XML or JSON. According to MSDN, the determination is based on the following in order:
- The media types in the request message’s Accept header.
- The content-type of the request message.
- The default format setting in the operation.
- The default format setting in the WebHttpBehavior.
Using Fiddler, I’ve set the Accept header to application/json which has the result of returning JSON without me having to do anything to the service itself:
Of course, you can have the service return plain text or a stream, but to do so today, you need to implement a custom mechanism for passing the format you need such as a querystring and then writing some code to read it and return the appropriate expected format.
Finally, one of the biggest benefits of designing REST services, or services that leverage the HTTP protocol exclusively, is that you get all of the features inherent to the HTTP protocol including support for caching requests, or GET requests. By adding an attribute called AspNetCacheProfile to the GetCollection method and mapping the profile name in the web.config, I can leverage the caching capabilities in ASP.NET to cache GET requests:
1: [WebGet(UriTemplate = "")]
2: [AspNetCacheProfile("MyCacheProfile")]
3: public SingleTracks GetCollection()
4: {
5: return GetSingleTracks();
6: }
As I hope I’ve shown, there is pretty strong support for REST in WCF 4, both in the box and as provided by the WCF REST specific templates which allows you to easily host your WCF REST services on any host, including Windows Server AppFabric which allows you to take advantage of all of the great management and monitoring capabilities it provides. New features in the WCF Web API will make it even easier to host HTTP/REST services with WCF, so watch this space for some upcoming posts and be sure to keep up with the latest developments on the WCF Community Site at http://wcf.codeplex.com/.