Improving the Web API routing system

Web development has my attention, the guys at Microsoft somewhat surprised me with the speed at which they innovate on the ASP.NET platform with the new Razor pages, ASP.NET MVC 4 and Web API.

Web API is a new framework that you can use to build RESTful services for your webapplication. It’s a very powerful framework that allows you to customize a lot of stuff without sitting in the way too much. For example, you can customize the way the urls for your RESTful services are layed out by applying custom routes.

In this post I will show you how the routing system for Web API works and what I did to improve the experience a bit.

What was the ASP.NET routing system again?

As many developers that use ASP.NET MVC know, you can customize the urls for your website by adding custom routes to the application in the Application_Start handler in the global.asax file of your website. ASP.NET grabs these routes and matches them against the incoming URL. If a route matches the URL, the handler for that route is used to handle the request.

You can create routes that handle various patterns in the URL, like the following:

  • {controller}/{action}/{id}
  • categories/{name}
  • events/{eventId}/sessions/{sessionId}

Very handy to get those fancy URL’s you see on modern weblogs and webshops these days. Or anywhere elese for that matter.

Note: On a side note; You may wonder why would need these fancy urls. The reason is simple: Search engines. They like them alot and your users like them too. It’s easier to remember a meaningful URL than one that contains a GUID or a lot of query string parameters.

Creating custom routes in ASP.NET

To use a custom route you simply add one to the routing table when your application starts. The following example demonstrates a very basic route configuration for ASP.NET MVC.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapRoute(
        name: "MyRoute",
        url: "pages/{controller}/{action}/{id}",
        defaults: new {id = RouteParameter.Optional});
}

Note: While the routing system is used heavily in ASP.NET MVC, it doesn’t mean you cannot use it anywhere else. The same engine can also be used inside a typical webforms application as well, to make the pages easier to remember. But instead of using the MVC handler, you should choose a page handler for each route you add.

Routing inside Web API

Just as you would route a controller inside ASP.NET MVC you can route API controllers in Web API applications, by adding custom routes to the route table. However instead of using the MapRoute method you should use the MapHttpRoute method. This method will attach a HttpRouteHandler instance to the route instead of the regular MvcRouteHandler which is meant for processing ASP.NET MVC action results.

The following example demonstrates the use of the MapHttpRouteMethod:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

Apart from the method name, there isn’t any difference in the way you invoke the method. This makes it easy for developers to step from Web API over to MVC and back again, without actually having to learn a new way to route requests.

Everything is green and lovely or…

Well, it is outside, but maybe not so much in Web API land. When I converted an old WCF service into a Web API RESTful service I quickly learned that all those custom routes make your code one ugly piece of junk. Imagine having 210 lines of code in a single method and not being half-way done converting your old webservice. It means bad news for the maintainability of your application.

There’s two things wrong with the routing system at this point:

  1. You get one big method containing all the custom route entries. Which makes your code less readable if not unreadable.
  2. There is no way you’re going to find which route is meant for which controller action.

I was hoping Microsoft at least would bring the attribute based configuration back from the WCF web services API, but apparantly they didn’t.

Making a fix

To solve the problem I created an extension that allows you to do the following in your code:

[HttpRoute(UriTemplate = "api/workshops")]
public IQueryable<Workshop> GetWorkshops()
{
    return _repository.GetWorkshops();
}

This does two things; One it creates less clutter in your Global.asax.cs file; Two it tells you exactly which route gets you to which method on a controller. All in one neat package Glimlach

To get the routes defined on your controllers into the routing table, you need to add one small piece of code to the startup of your application.

public static void RegisterRoutes(RouteCollection routes)
{

    // Use the route table builder to reflect routes
    // defined on the controllers in the application
    HttpRouteTableBuilder.BuildTable(routes);

    // Add a default route as fallback
    routes.MapRoute(
        name: "MyRoute",
        url: "pages/{controller}/{action}/{id}",
        defaults: new {id = RouteParameter.Optional});
}

This will reflect over all the API controllers in the application and extract the routing information from them. It does this only once, during startup so you shouldn’t notice a big dip in performance when using this utility.

You can use the HttpRoute attribute to specify a route for a specific method, but you can also use it to specify a specific route for a controller as a whole. Web API doesn’t really need a specific method to route a request as long as there’s a method that supports the HTTP Get verb.

Last but not least, you can tell the routing system that certain parameters inside a route are optional. For example, you have a route that handles an URL with both the ID parameter and without the ID parameter. This can help reduce the number of methods or make certain, more complex, scenarios possible. The HttpRoute attribute also supports this scenario, by allowing you to specify the [OptionalRouteParameter] attribute for a method parameter. This way the HttpRouteTableBuilder class  knows that the parameter is optional and it will configure the routing system as such.

Where can I get the goods?

You can get the goods right here on my weblog and they are free for you to use anywhere you like. And if you’d like this in the form of a Nuget package, just give me a yell and I will see that one is created.