ASP.NET Web API Routing needs a hug

One of my goals with the WebAPIContrib is to be able to write as few code as possible in my API application and let the Contrib help me with the boring boilerplate code.

Looking through the ASP.NET Web API samples, tutorials and blog posts out there, the first thing that jumps on my eye is the whole HttpResponseMessage noise.

I don’t want to have to write all these status code and Location Header settings all over my code. This is boring and adds noise to what actually is important on my controller action. So, my idea was to create objects to represent each status code, something similar to what Restfulie does, and encapsulate all this noise inside these specialized HttpResponseMessages.

I imagined doing something similar to:

But, in the create response I also want to set the Location Header of the new resource. As a first pass, I went with:

Now, in the API code I can have:

Although this achieve my goal of setting the status code and Location header in the HttpResponseMessage I’m still not happy. Mainly because of manually building the URI of the Location. I don’t have to hardcode URLs when I can use the type system to help me with that.

Embracing the type system, or not.

I’m really not happy with hardcoding the URI to set the Location Header. A better way of setting that would be:

With that, all I would have to do is generate the URL for that controller action and set it to the Location Header. Piece of cake, it’s just use the built-in UrlHelper. Or, that’s what I thought.

The System.Web.Http.Routing.UrlHelper has a Route method that returns an URL for a set of route values. OK, so at first glance I just need to translate my Expression<Action<TController>> in a dictionary of route values. This is not a problem, MVCContrib, for example, uses a helper from the MVCFutures assembly to do this. As I’m working in the WebAPIContrib, I didn’t want to take a dependency in the MVCFutures just for this. I ended up coding the reflection part myself.

OK, no I have a RouteValueDictionary, to get the URL back is just call urlHelper.Route(routeValues), right? Not so quickly.

Y’all gotta know the names. All the names

The Route method on UrlHelper is not like the Action method on it’s System.Web.MVC sibling (more on this Mvc x Http later). As you can see in the MSDN documentation, the Route method signature is:

Together with the route values you also have to supply the route name. As I’m building a library for others to use while building their APIs, I don’t know which route to use. My first thought was: I’m just going to iterate over the HttpRouteCollection and the first URL I get back would win. That’s when I started learning more about the ASP.NET Web API routing implementation than I was wanting to.

No foreach for you

When I tried to iterate over the HttpRouteCollection, the following exception was thrown:

Checking on Google, I found out that I had to get a read lock first, before iterating over the collection. This is done through the GetReadLock method that returns a disposable lock object. :SadTrombone: Fooled one more time by the Mvc vs. Http identity issues. Only System.Web.Routing.RouteCollection has the GetReadLock method. The HttpRouteCollection counterpart doesn’t have it.  So, I had to fall back to for loops to get the routes out of the HttpRouteCollection.

Then, another surprise. To refresh your memory, we are doing all this looping things in order to get the route name so that we can use the UrlHelper to get the Location header URI. So, after I was able to get a route object instance, I found out that there is no way to get it’s name. Yeap, you create a route with a name. The UrlHelper asks you for a route name. You can even check in the collection if there are any routes with a given name. But you can’t get the name from a route instance. With dotpeek’s help I figured out that the name is used as the key in the private dictionary that the HttpRouteCollection wraps. But the HttpRouteCollection doesn’t make the Keys property of the dictionaty available to the outside world. That’s it. There is no way for you to programmatically find a routes name.

Ah, the ControllerContext

At this point I was already pretty frustrated. I was working on this during the Dallas Day of .Net and I was afraid I wouldn’t be able to keep cursing only in my head anymore. Actually I believe I said one or two “WTF!” loudly. Anyways, I gave up the whole let’s-find-out-the-route-name-thing and decided to hard code the default one instead. Just so that I could at least see the thing working.

To my despair, I realized that the System.Web.Http.Routing.UrlHelper depends on the HttpControllerContext, not in the RequestContext as System.Web.Mvc.UrlHelper. As I can’t simply create, or at least not as easily, an instance of the HttpControllerContext as I can create one of the RequestContext, I just changed the constructor of the CreateResponse to also include the HttpControllerContext.

I’m definitely not happy with this and I won’t include any of it, as it is, in the WebAPIContrib.

Least Surprise who?

Let’s be clear: this whole thing of System.Web.Mvc vs. System.Web.Http is confusing as hell. Things have the same name but are different types. The api for those types are different. They behave differently. I can see a lot of people struggling as I did when trying to use whatever they are using in their MVC projects in a Web API one.

But the Web API is still in beta and this is what a beta is for, right?

Yes, the ASP.NET Web API is still in Beta and I expect a lot of the library api to change. As Henrik Nielsen asked me to do, I’ll register the issues I had on user voice. And I can only hope they will fix it. Or maybe accept a pull request ;-)

Extending ASP.NET Web API Content Negotiation

The ASP.NET team released the beta version of the ASP.NET Web API, previously known as WCF Web API, as part of the beta release of ASP.NET MVC 4. Having experience implementing web APIs with Restfulie, I was curious and decided to check how the ASP.NET Web API works to compare it with Restfulie.

The first thing I noticed was a difference in the Content Negotiation implementation. I don’t intend to do a full comparison here, but to describe how to use one of the extension points in the Web API to add the behavior that I wanted.

If you are not interested in in the reasons why the Content Negotiation implementation differs, and why I prefer the Restfulie one and just want to see DA CODEZ, feel free to jump to the “Message Handlers to the Rescue” part.

Content Negotiation

Content Negotiation, simply put, is the process of figuring out what is the media-type that needs to be used in the Resource Representation that is going to be returned in a HTTP Response. This negotiation between client and server happens through the interpretation of the HTTP Accept Header values.

When a client makes a request, if the client wants to specify a set of media-types that it accepts the resource to be formatted into, then these media types should be included in the Accept Header. All the semantics and options regarding the usage of the Accept header can be found on section 14.1 of RFC 2616 (HTTP specification). In the Accept Header definition there is a part that reads:

If no Accept header field is present, then it is assumed that the client accepts all media types. If an Accept header field is present, and if the server cannot send a response which is acceptable according to the combined Accept field value, then the server SHOULD send a 406 (not acceptable) response.

The SHOULD key word in the RFC 2616, according to section 1.2 of the RFC, is to be interpreted as described in the RFC 2119. The description of SHOULD in this RFC is:

SHOULD   This word, or the adjective “RECOMMENDED”, mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.

With this definition, I interpret the Accept Header behavior as: unless you have a specific reason not to, if the HTTP Request contains an Accept Header and the server does not support the requested media-type, a 406 – Not Acceptable response should be returned.  Returning other Status Code might be OK, as it’s not mandatory to return 406, but this should be the exception, not the default behavior.

ASP.NET Web API Content Negotiation Implementation

The namespace System.Net.Http.Formatting has a MediaTypeFormatter class whose instances are used by the Web API (I’m going to omit the ASP.NET part from now on) to format the resource representation to a specific media-type.

The Web API ships with JsonMediaTypeFormatter and XmlMediaTypeFormatter implementations. And it defaults to the JSON formatter when no specific media-type is requested. So, if the Accept Header is not included in the HTTP Request, Web API will use the JSON formatter to format the Resource Representation that is going to be returned.

You can add your own MediaType formatter by adding your formatter implementation to the MediaTypeFormatter collection of the HTTPConfiguration object in the GlobalConfiguration class.

It seems like something is not right here

While I was playing with the Web API, I decided to test the behavior of the Content Negotiation implementation in the scenario where the media-type in the Accept Header is not accepted by the server. That is, when the HTTP Request Accept Header specifies a media-type that the Web API doesn’t have a MediaTypeFormatter implementation that can handle it. By the definition described in the Content Negotiation section above, I expected to receive a 406 response back. To my surprise, I got a 200 – OK response back with a Resource Representation with text/json contant-type. Web API fell back to the default MediaTypeFormatter instead of returning 406.

This was a surprise to me, so I filled a bug report for this in the Web API Forum. Henrik Frystyk Nielsen replied saying:

It’s a reasonable to respond either with a 200 or with a 406 status code in this case. At the moment we err on the side of responding with a 2xx but I completely agree that there are sceanarios where 406 makes a lot of sense. Ultimately we need some kind of switch for making it possible to respond with 406 but it’s not there yet.

Fine. The 406 response is not mandatory, the Web API is still in beta. I understand the 406 handling not being there yet. And, the last thing I want to do is to get into a discussion of RFC semantics with one of the authors of the specification  :P

Message Handlers to the Rescue

When a HTTP Request is made to a Web API application, that request is passed  to an instance of the HttpServer class. This class derives from HttpMessageHandler and it will delegate the processing of the HTTP request to the next handler present in the HttpConfiguration.MessageHandlers collection. All handlers in the collection will be called to process the request, the last one being the HttpControllerDispatcher that, as you can tell, will dispatch the call to an Action in a Controller. FubuMVC Behavior Chains implements an approach similar to this. (Fubu guys, I know it’s not the same thing, but y’all get my point :))

With that said, in order to return a response with 406 status code, what I need to do is implement a HttpMessageHandler that does that.

With the handler created, the next step is to configure Web API to include this handler in the HTTP Request processing pipeline. To do so, in the Application_Start method in the Global.asax , add an instance of the new handler to the MessageHandlers collection.

With the application configured to use the custom handler, when that same request is made, a response with 406 Status Code is now returned.

The power of Russian Dolls

The Web API HTTP Request pipeline provides a powerful extension point to introduce specialized, SRP adherent, handlers keeping the controller action implementation simple and focused.