Tuesday, 27 November 2012

IIS / httpErrors / ASP.NET Web API / Fully embracing HTTP in Web API and having a professional web application is mutually exclusive.

Hi there, I’m going to get to the point really quickly because I’m very busy. 

With ASP.NET Web API introduced in .NET 4.5 we’re encouraged to embrace HTTP as a protocol fully with RESTful APIs, or indeed more RPC-esque APIs.  When we don’t find a record inside the database we can do this:

   1: Request.CreateErrorResponse(HttpStatusCode.NotFound, "The record was not found");

Aint that beautiful?  If you don’t appreciate it; well you should because now, an error response is returned to the client in a data format that the client/browser requests.  So if you’re interacting with the API via JSON then you’ll get back a JSON formatted response. Same with XML etc.

The idea is we utilise HTTP response codes (200, 404 etc) to represent a plethora  of different types of response. Before we might just return error codes embedded inside the response and the HTTP response status would probably be 200 (OK).

Hold all that in your short term memory for a sec.

With IIS7+ we’re encouraged to embrace the new httpErrors section inside web.config. With this it means you can return friendly error messages to the client/browser.

   1: <httpErrors defaultResponseMode="File" errorMode="DetailedLocalOnly" existingResponse="Replace" xdt:Transform="Insert">

   2:   <clear />

   3:   <error path="html\system_error.html" responseMode="File" statusCode="400" />

That’s great.  Now you don’t have IIS specific error messages going to the client / browser.  Not only does this mean your web app can fail slightly more gracefully and helpfully (to the user), but also, it’s vastly more professional looking.


Above I have described two great features of the MS web stack.  Unfortunately, they are mutually exclusive.  You cannot host Web API inside the same project as a web app and have these two items function.  IIS will hijack your Web API responses because of the attribute on the httpErrors node existingResponse=replace.  If you get rid of this attribute and use PassThrough, then your Web API responses will get through OK.  However, now if you have an error in the website outside of the scope of the web api, then the client will see non-custom error messages (might be detailed or not, depending on the errorMode).

So I have reverted back to returning status HTTP 200 for all Web API business exceptions.  In reality, the the API client is not looking at the HTTP status code anyway, it’s looking at the returned content-type (if it’s the expected content-type, e.g. JSON), then it’ll look inside the JSON envelope (see JSend / AmplifyJS) and figure out was the real API response was.  If the content-type was not expected, then the API client will assume something worse happened.  It’ll examine the HTTP status code and I actually have error codes inside the custom IIS error pages, so that the API client can do a simple string search inside the html content to see what the overall problem was.  But hey, that’s very much application-specific stuff.  Each app will do it differently.

However, in any case, it does appear that custom error messages AND formatted Web API error responses are mutually exclusive for the time being.  If anyone knows how to do both without having to split the project into an API project and the web project with the their own separate configurations, feel free to comment.