Now that ASP.Net MVC 2.0 is out, I’ve upgraded the RESTful Web Service to use it. I’ve also taken this opportunity to round up the features we have built into the RESTful web service support so far.
Upgrading to MVC 2.0
The only code changes I made were to use the [HttpGet], [HttpPost], [HttpPut] and [HttpDelete] attributes rather than the old style [AcceptVerbs(HttpVerbs.Get)], [AcceptVerbs(HttpVerbs.Post)], [AcceptVerbs(HttpVerbs.Put)] and [AcceptVerbs(HttpVerbs.Delete)] attributes. These were changes to the example web site. The Shoulders of Giants assembly which provides the RESTful features was unchanged.
Allowing the same resource to be represented in a number of ways is important if you want to enable the widest range of client technologies to use your web service. Way back in my third post I first discussed supporting more than one representation. I started with XML, JSON and XHTML then in the fourth post I introduced one more representation, HELP, which would return XHTML with extra documentation and a test harness.
Those early versions of the web service had a fixed list of representations. In the nineteenth post I took an idea from the the Rest for ASP.NET MVC SDK an introduced more flexible representation support. By default XML, XHTML, JSON and HELP are used. But now you can remove format handlers and add your own by registering them on application start up:
XmlFormatHandler xmlFormatHandler = new XmlFormatHandler();
As discussed in part 8, there are clients that cannot use all the HTTP Verbs we might want to support. For example PUT and DELETE are generally not supported by browsers and when using HTTP Basic Authentication, Flash only allows POST.
One technique often used to circumvent these restrictions is to use a POST, but indicate the actual verb the client wants to use in some other way. Initially in part 8 and part 9 the web service only supported using a query string parameter to provide verb information. In part 19 this was extended to the following approaches for overloading POST:
- Add the verb to a request header. By default the web service will check the X-Http-Method-Override header. So the following would cause the web service to perform a delete:
- X-Http-Method-Override: DELETE
- If the request is a FORM post (i.e. it contains key / value pairs used when submitting a form) the web service looks for a FORM variable called X-Http-Method-Override. The value is then used as the verb
- The web service also looks for a variable called X-Http-Method-Override in the query string.
For all three approaches, it is possible to change the string used to look for an overload. For example, putting the following in the application start up will make the web service look for a query string variable called _method instead of X-Http-Method-Override:
RestfulHttpRequestWrapper.HttpMethodOverrideQueryStringId = "_method";
Note: _method is the variable often used in other frameworks like Ruby on Rails.
Initially, the web service only supported the client dictating which format the response should be in by passing a format variable in the Query String. In the nineteenth post, by providing a new HttpRequestWrapper, this was extended to allow the client to use any one of these methods:
- Passing a format parameter in the query string (the name of the parameter can be set, but defaults to “format”);
- Adding an Accept header to the request dictating one or more Content Types it can accept. These can include quality factors to allow preferences to be provided (see the RFC-2616 specification). For example a client could send the following to indicate it accepts JSON or XML but prefers XML:
- Accept: application/json; q=0.1, application/xml; q=0.8
- If the previous two methods have not been used, the web service will default to the content type of the request
- If none of the above are used, the web service uses the default request content type, which, as per RFC 2616 7.2.1 is application/octet-stream.
My preferred approach is still the query string (see the Multiple Representations post for my reasoning) but now there is a lot more flexibility.
According to RFC 2616, the Accept Charset header allows a client to state a preference for the encoding that should be used when returning a response. I addressed this initially in post 16 where I ensured the XML representations were encoded correctly. In post 20 I came back to this issue to make sure JSON was also encoded correctly. Post 20 also handled the client sending a choice of charset with preferences. So the following header would result in the response being UTF-16 encoded:
- Accept-Charset: utf-8; q=0.2, utf-16;q=0.8
If the client does not send an Accept-Charset header, the web service uses the encoding that the client has used to make the request.
Along with supporting the Accept and Accept-Charset headers that a client can send along with a request, the format handlers provided “Out of the box” also set a Content Type header in the response to indicate the actual content format and encoding used in the response. For example:
- Content-Type: application/xml; charset=utf-8
The Allow header (as defined by RFC-2616) can be used by the web service to indicate which HTTP Verbs are valid against a particular resource. If a controller implements the IAllowedVerbs interface, it can supply a string to be returned in this header… this is described in post 20.
As I described in post 14, one of the tenants of a RESTful web service is connectedness. A well connected service allows the client to move through the application with minimal prior knowledge. I introduced a way to inject links into XML representations and in post 15 I showed how this could be achieved with a JSON representation.
Had AutoMapper been available at the time, that would perhaps have been a better solution. Perhaps in the future I will drop my approach in favour of AutoMapper.
Creating Your Own Web Service
Look at the example web service included in the download. The main points are:
- Create Controllers and Views as per usual
- Decorate the Controllers with [RestEnabled]
- Derive your controllers from IAllowedVerbs if required
- Use MapRestfulRoute instead of MapRoute in the application start up
- Create XSLT files alongside your ASPX files
- Handle Application_Error and create an Error Controller
- When a controller needs to return a specific HTTP Code, use the View extension method provided that accepts an extra HttpStatusCode parameter
In my next post I will look at the new features in ASP.Net MVC 2.0 and the MVC Futures assembly that comes with it to see how they can be used to provide a RESTful web service. MVC Futures includes some of the features first trialled in the Rest for ASP.NET MVC SDK I reviewed previously. So I’ll look how it is coming along. It might be that by ASP.Net MVC 3.0 arrives, our RESTful extension will no longer be needed!
Download the code here: RESTfulMVCWebService21.zip (161.24 kb)