Creating a RESTful Web Service Using ASP.Net MVC Part 5b – HTTP Response Codes

October 16, 2008 20:43 by admin

In this series of posts we have been using ASP.Net MVC to create a RESTful web service. In my last post I looked at returning the full set of standard HTTP Response codes. At the end of that post, the code was handling errors raised during the processing of an action. However, as I said then, that code would not catch all possible errors that are raised during the processing a request.

In this post I’ll look how our web service can handle those other errors instead of relying on the ASP.Net framework to handle them.

In the last post we used the handlers provided within the MVC framework (e.g. the OnException method available on a controller). However, there are some errors raised within the MVC framework for which there are no built in handlers. For instance, if a request cannot be resolved to a controller then a HttpException, with a code of 404 – Not found, is raised. These errors can be handled in the same way any other ASP.Net error could be handled. Here are three more places we could catch errors:

  • Create a “Catch All” route for URIs that don’t match any of the other routes;
  • Provide an Application_Error in the Global.asax which will catch all unhandled errors;
  • Use the customErrors section in the web.config file to redirect to an error page.

The Catch All route is worth providing but its usefulness is limited. If any of the preceding routes match (however inappropriately) the requested URI, then it will not be triggered. I updated the RegisterRoutes method in Global.asax.cs to:

public static void RegisterRoutes(RouteCollection routes)

{

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 

    routes.MapRoute(

        "Default",                                              // Route name

        "{controller}/{action}/{id}",                           // URL with parameters

        new { controller = "Home", action = "Get", id = ""// Parameter defaults

        );

 

    routes.MapRoute(

        "CatchAll",

        "{*url}",

        new { controller = "Error", action = "Http404" }

        );

}

Since the GuidController is the only controller so far, you might hope that all other requests would be sent to the ErrorController. Unfortunately, only requests that are not in the form of {controller}/{action}/{id} are handled by the ErrorController. These requests:

  • http://localhost/ShouldersOfGiants/RESTfulMVCWebService/Version02/a
  • http://localhost/ShouldersOfGiants/RESTfulMVCWebService/Version02/a/b
  • http://localhost/ShouldersOfGiants/RESTfulMVCWebService/Version02/a/b/c

will trigger the default ASP.Net “Resource not found” error. Only when we get to:

  • http://localhost/ShouldersOfGiants/RESTfulMVCWebService/Version02/a/b/c/d

will the ErrorController be used to handle the request.

Using customErrors is a non-starter because ASP.Net actually uses a client side redirect to send the client to the error page. Therefore, rather than get a 404 – Not Found, the client will get a 302 – Found (effectively a redirect).

The last place to catch all errors is Application_Error in Global.asax.cs. In a ASP.Net Web Forms application we could use Server.Transfer within Application_Error to have an error page display. However, code along the lines of:

void Application_Error(object sender, EventArgs e)

{

    Server.ClearError();

    Response.Clear();

    Server.Transfer("xyz");

}

throws an HttpException, "Error executing child request for xyz." with an inner HttpException exception, "No http handler was found for request type 'GET'". It looks like Server.Transfer is not supported in the MVC framework! The solution I chose was to manually execute an action on a specially created ErrorController:

void Application_Error(object sender, EventArgs e)

{

    Exception exception = Server.GetLastError();

    HttpException httpException = exception as HttpException;

 

    RouteData routeData = new RouteData();

    routeData.Values.Add("controller", "ErrorController");

    routeData.Values.Add("action", "Http500");

 

    if (httpException != null)

    {

        if (httpException.GetHttpCode() == 404)

        {

            routeData.Values["action"] = "Http404";

        }

    }

 

    Server.ClearError();

    Response.Clear();

 

    IController errorController = new ErrorController();

    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

}

This may not be the most elegant solution, So I’m open to suggestions of a better approach. However, once in place, we have full control of the HTTP Response Codes that the web service returns.

The source code is available for download, RESTfulMVCWebService03RC2.zip (33.83 kb) (Updated for Release Candidate 2). In my next post I want to start looking at some of the other HTTP verbs.

kick it on DotNetKicks.com


Comments (6) -

October 17. 2008 03:54

Jarrett

If you'd like to see the ultimate example of a RESTful web service built using asp.net mvc then you should checkout Blog Service.  See the AtomPubController for a great example that uses  GET POST PUT DELETE and even HEAD.  Also, there are some custom ViewResults that can return xml results or basic http results.

Jarrett

October 17. 2008 17:14

Piers

Blog Service is interesting stuff, but I'm not sure it addresses the issues I am looking at in this series. My next post will look at the other verbs... so far I've just been warming up with different representations of resources, response codes etc.

Piers

October 21. 2008 20:01

Andy

Now with the IValueProvider interface in MVC Beta, for getting params from a POST, I've been wondering whether an XmlValueProvider could be written to get values from raw XML in a POST body.

Andy

October 21. 2008 22:18

Piers

Thanks for the suggestion. I'll be getting onto interpretting incoming data fairly soon, so I'll look into that as an idea.

Piers

May 5. 2011 08:53

John

Great thinking thanks for the post. Will check your adverts for sure Smile

John

May 5. 2011 13:46

Jim

Nice, thanks for sharing

Jim

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading