Creating a RESTful Web Service Using ASP.Net MVC Part 20 – Injecting RESTfulness and Better Encoding

January 16, 2010 00:42 by admin

In this series of posts, I have concentrated upon creating a truly RESTful web service… it has not been a priority to use the full power of ASP.Net MVC’s extensibility. However, this has resulted in a solution that is too intrusive into ASP.Net MVC. The consequences being, any web service developed with the Shoulders of Giants DLL can be difficult to write unit tests for or extend in other directions… so with this and my previous post, I hope to address this.

My last post removed the need for the AcceptsVerbsAndOverloads attribute. By injecting a RestfulHttpRequestWrapper into the solution, the overloading of POST and handling of content types is dealt with in a way that is more in tune with ASP.Net MVC. It also removes the need for a web service developer to use a custom attribute on the actions in their controllers.

However, more intrusive than the attribute, is the requirement for the web service developer’s controllers to derive from RestfulController. RestfulController selects which ActionResult to return and also provides methods for deserializing incoming representations. Removing this restriction should make creating a RESTful web service a much lighter touch… but how to remove it?

The first thing to do is to consider the extensibility points that ASP.Net MVC provides. I have not been able to find an official list on a Microsoft site, but Simone Chiaretta has listed 13 of them. He doesn’t mention providing a version of MvcHandler (which we used in the last post to inject RestfulHttpRequestWrapper) but its a good start. Number 12 on his list is the View Engine.

View Engine

A View Engine is responsible for finding / creating a View. For example the default WebFormViewEngine will locate the ASPX (or ASCX) file required to render a View (or Partial View) and initialise a View with the file’s location. It is the View’s responsibility to render a response. You can register additional View Engines at application start up. To support JSON, XML and XHTML, this might seem the perfect solution… if we created an XsltViewEngine and JsonViewEngine, they could look for XSLT files or registered JSON Serializers in much the same way the WebFormViewEngine looks for ASPX files. An XsltView or JsonView could then be used to generate a response.

However, there was one major issue I had with this approach. I wanted to start supporting the Accept header. This header allows a client to list which content types it can accept as a response (with an order of preference). Unfortunately, the framework iterates through the registered View Engines only once, giving each engine only a single chance to handle a request. So for our scenario, each View Engine would have to look at the Accept header and decide if it could support any of the acceptable types. This would cause two issues:

  • Each View Engine would have to repeat the Accept header parsing and sorting
  • An individual View Engine would not know if any of the other View Engines could provide a better match of content type. So the first engine to find any match would be used. This would mean the response would be of an acceptable content type, but not necessarily the preferred type.

Really, I wanted to be able to loop through the View Engines multiple times, once per acceptable content type, in the order dictated by the client. This would allow us to return a representation formatted in the content type that best matched the clients request. One possibility is to create a super View Engine that contains custom logic to iterate over child View Engines (I believe an ASP.Net MVC Beta had an AutoViewEngine that worked like this).

Basically, the View Engine approach doesn’t quite fit and if I implemented a workaround I’d be moving further from the default ASP.Net MVC processing model.

Action Filter

So if View Engines are not the solution, how else can we inject our own rendering functionality? One approach already used in the ASP.Net MVC framework is to use specific Action Result classes such as JsonResult. In Version 16 of our web service we also used Action Results (XsltResult and JsonConverterResult). The problem with this solution is that the controller has to decide which Action Result to use and create it explicitly. This puts more responsibility than is healthy into the controller… this is MVC after all and ideally the controller has no knowledge of the final representation format. Trying to remove this responsibility from the code written by the developer led to base class RestfulController in previous versions.

There is however, one extensibility point that is often used to change the Action Result returned by a Controller… Action Filters are executed before and after an action is executed on a controller. An Action Filter is brought into play as an attribute on a controller. I created the RestEnabledAttribute and implemented the OnActionExecuted method.

The OnActionExecuted method replaces the Action Result created by the controller through the following process:

  • Create a list of supportable ContentType objects acceptable to the client, ordered by client preference
  • For each Content Type, in turn, have the FormatManager try to create a replacement Action Result
  • If a suitable Action Result is created, the original Action Result is swapped out… otherwise an exception is raised
foreach (ContentType contentType in 
                  GetAcceptHeaderContentTypes(filterContext.HttpContext.Request.AcceptTypes))
{
    replacementResult = FormatManager.Current.TryCreateActionResult(viewResult.ViewName, 
                                                                    viewResult.ViewData.Model,
                                                                    contentType, 
                                                                    charsetList);
    if (replacementResult != null)
    {
        break;
    }
}

if (replacementResult == null)
{
    throw new HttpException((int)HttpStatusCode.NotAcceptable, "None of the formats ...");
}

Each time it is asked to find a suitable replacement Action Result, the FormatManger iterates over a list of IResponseFormatHandlers asking each if it can create an Action Result that supports the ContentType:
 

foreach (IResponseFormatHandler potentialResponseFormatHandler in ResponseFormatHandlers)
{
    result = potentialResponseFormatHandler.TryCreateActionResult(name, model, 
                                                                  contentType, 
                                                                  acceptCharsetList);
    if (result != null)
    {
        break;
    }
}

Note: a default set of handlers are added to the list, but you can add your own, or take some away.

The slight downside of this approach is that we throw away the original Action Result returned by the controller… a slight waste but for now I think I can live with it.

Model Binder

Another area that must change is how we deserialize an incoming representation. Previously the RestfulController provided a generic method for deserializing representations. We can replace this functionality with an implementation of IModelBinder. A Model Binder is responsible for binding inputs taken from the request, to parameters on a controller’s Action. Previously, our Actions didn’t have parameters to accept the deserialized content, deserialization was performed within each Action method. By using a Model Binder, we remove one more responsibility from the developer writing the controller and finally remove the last reason for the controller to derive from RestfulController..

However, the previous approach had one advantage… the RestfulController’s generic deserialization method meant the type information was provided at compile time. The new approach means type information is provided dynamically by the ASP.Net MVC framework. Unfortunately the JavaScriptSerializer we use to handle JSON representations doesn’t provide a simple way to specify type information dynamically. In my look at the MVC SDK I solved this without resorting to using reflection to set private properties. I have brought that solution into this version.

I have created an implementation IModelBinder called RestfulModelBinder. It only binds parameters that are:

  • On a controller that has the RestEnabledAttribute Action Filter
  • Not read from the URI (i.e. they are not part of the routing information)

Any other parameter binding is passed to a fallback binder provided in the constructor. Here is the code that performs the binding:

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    // Only try to handle binding to controllers that want to be dealt
    // with in a RESTful manner
    if (RestEnabledAttribute.IsDefined(controllerContext.Controller))
    {
        // Only bind values that have not been extracted from the URI if the request
        // actually has a body to deserialize
        if (!controllerContext.RouteData.Values.ContainsKey(bindingContext.ModelName) && 
            RequestHasBody(controllerContext.HttpContext.Request))
        {
            object model;
            if (!TryBindModel(controllerContext, bindingContext, out model))
            {
                string requestFormat = controllerContext.HttpContext.Request.ContentType;
                throw new HttpException((int)HttpStatusCode.UnsupportedMediaType, "…");
            }

            return model;
        }
    }

    return this.inner.BindModel(controllerContext, bindingContext);
}

The TryBindModel method hands off responsibility for binding to the FormatManager. The FormatManger loops through its collection of IRequestFormatHandlers to find one that is prepared to deserialize the request body.

IAllowedVerbs

One of the limitations I identified with my web service developed using the MVC SDK was that I couldn’t easily support the Allow header in the response. This is a feature I first introduced in my Filtering Verbs post.

To ensure this functionality was still supported, I introduced the IAllowedVerbs attribute which a controller can derive from. If a controller implements this interface, the RestEnabledAttribute Action Filter will use the AllowedVerbs property to populate an Allow header in the response.

Encoding

As I worked through these changes I realised that I wasn’t dealing with Encoding as well as I could. I first discussed encoding in my XML Encoding post. At that point I only considered encoding of the XML data, not JSON and I allowed the XSLT to dictate the encoding to be rendered (as opposed to allowing the client to ask).

I decided to implement the following:

  • Support the Accept-Charset header when processing a request
  • If Accept-Charset is not provided, default the encoding to the encoding provided in the request
  • Have the JSON serialization support encoding
  • Specify the encoding used in the Content-Type header of the response

Accept-Charset is a bit of a misnomer. It is actually a header that allows the client to indicate its preference for encoding of the response. RFC 2616 specifies the client can provide a list of encodings and can state its preference.

I created a Charset and CharsetList classes that would parse and sort the Accept-Charset header (these were heavily based on the generic header parsing classes written by Dave Transom). The RestEnabledAttribute Action Filter uses these classes to create a list of preferred Charsets to be used by the FormatHandlers when rendering a response. It appends to this list, the encoding used by the client to make the request.

The JsonActionResult had to be adapted to deal with encoding. Unfortunately the JavaScriptSerializer does not use streams or support encoding, so some in memory manipulation of strings and byte arrays was required. Other changes required included:

  • The Xslt class was also updated to force the encoding in the XmlWriterSettings object used when transforming data;
  • XmlActionResult and JsonActionResult add the encoding name to the response so that the Content-Type header returned to the client includes a charset parameter.

Following these changes, the web service will use best endeavours to return a response using an encoding that is acceptable to the client.

Breaking Changes

I’ve made a fairly big changes with this version but hopefully most of the changes required to use this version will be simplifications. Here is a list of the changes you may need to apply:

  • Instead of deriving your controllers from RestfulController, decorate them with [RestEnabled]
  • If you want to support the Allow header, derive your controller from IAllowedVerbs and implement the AllowedVerbs method… this should have the same implementation as your existing AllowedVerbs method that RestfulController forced you to provide
  • Each of your controller’s Actions that handle incoming representations, should take a parameter to receive that representation… rather than the previous version which required you to call DeserializeIncomingRepresentation
  • In Global.asax.cs register the new Model Binder:

 

protected void Application_Start()
{
    // Hook up the RESTful binder instead of the default
    ModelBinders.Binders.DefaultBinder = new RestfulModelBinder();

    // Change from the default parameter used to override POST
    RestfulHttpRequestWrapper.HttpMethodOverrideQueryStringId = "_method";

    // Change from the default Content Type to use when the client doesn't supply one
    RestfulHttpRequestWrapper.DefaultContentType = "application/xhtml+xml";

    RegisterRoutes(RouteTable.Routes);
}

I’m running out of steam writing this post…. so here is the latest code RESTfulMVCWebService20.zip (123.25 kb).

Hopefully, with this version, we have the ability to create web services that not only adhere to RESTful principles but also sit well within the ASP.Net MVC framework… allowing you to get on with creating functionality without worrying (too much) about the REST aspects. Let me know what you think!

kick it on DotNetKicks.com


Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading