Attribute Routing in Web API v2

Attribute routing solves a lot of the problems of classic ASP.NET routing, which can get ungainly when there are many route handlers, or you have several REST services. Dino shows how to enable and use attribute routing in ASP.NET MVC

Today we’re all used to software services exposed over HTTP. We also expect these services to be available as plain HTTP callable endpoints: no proxies and no special API to sit in between as a mediator. This was not always the case, as you may recall.

When WCF Came on Stage

In the Microsoft stack, at some point we’ve been given WCF as the framework to use to expose externally callable endpoints. WCF was a great platform: extensible, flexible, scalable and probably close to perfection. A few years later, though, some people raised the point that maybe WCF was a bit over-engineered and that what had to be made easier and faster for WCF to meet actual needs  was just exposing HTTP callable endpoints. WCF was then topped with a long list of additional frameworks and starter kits to make programming HTTP endpoints easier. But easier coding doesn’t necessarily mean faster code.

ASP.NET MVC was particularly welcome also because it features controllers that can return JSON or XML. In a nutshell, in ASP.NET MVC you can have HTTP services at sole cost of adding a new controller class or a specific method to an existing controller class. This has always worked since version 1 of the framework. Controllers are much easier to set up than a WCF service and run as fast as reasonably possible. So WCF was quickly restricted to where the transportation had to be TCP or MSMQ, but not so much with HTTP.

Then It Came Web API

Problem solved? You bet! Even though ASP.NET MVC works very well, it still leaves something to be desired. ASP.NET MVC is tied closely to the ASP.NET framework and IIS. Web API came out to turn the good things in ASP.NET MVC into a new framework for HTTP services. The Web API framework relies on a different runtime environment that is totally separated from that of ASP.NET MVC. The runtime environment is inevitably largely inspired by ASP.NET MVC but, overall, it looks more straight-to-the-point as it is expected to only serve data and not markup.

Web API is close to its version 2 release. Version 2 is an evolutionary new release that enables and simplifies a few new features in Web API that were already available to ASP.NET MVC web developers. An example is external authentication now available through services such as Facebook and Google; another example is support for Cross-Origin Resource Sharing (CORS). Yet another example, and probably the most relevant, is attribute routing.

From a technical perspective, attribute routing is nothing special. Overall, it is a powerful feature that is relatively easy to explain and understand. However, the most interesting aspect of attribute routing in Web API is the reason why it exists and the original ideas it is based on. Let’s just start from here.

Routing and URL Templates

Attribute routing is not a feature that Web API inherits from ASP.NET MVC. Quite the reverse, attribute routing comes from old-fashioned WCF Web HTTP binding. Routing has always been a constituent part of ASP.NET MVC; attribute routing, instead, is a new variation of it.

Classic routing, which is still available in ASP.NET MVC 5 and Web API 2, is based on conventions. In global.asax, at the startup of the application, you populate a system dictionary with routes and let the system live with all of them. Any time a request comes in, the URL is matched against the template of registered routes. If a match is found, the appropriate controller and action methods to serve the request are determined from the template. If not, the request is denied and the result is usually a 404 message. The code below is taken from the default Visual Studio ASP.NET MVC project and shows the suggested way to register routes. The RegisterRoutes method is invoked from within Application_Start in global.asax

As a side-note, It also makes it easier for testing purposes to have a method prototyped just as RegisterRoutes. In a test you can call RegisterRoutes by passing a fake dictionary and then use the fake dictionary in the assertions.

There is another aspect, called route recognition, that is significant for the evolution towards attribute routing. Route recognition works on a first-match basis. The most immediate consequence of first-match is that the order in which routes are registered is essential. Most specific routes should appear first in the list; catch-all and most generic routes should go to the bottom of the list.

Why is this so significant? Well, in large applications, or even in medium-sized applications with a strong REST flavor, the number of routes may be quite large: and could easily be in the order of hundreds. If you’re a REST fanatic or if you’re just fussy about the URLs that your web application should support, then you may quickly find that classic routing becomes a bit overwhelming to handle. It can definitely become hard to determine the right order of more than 200 routes and you may find that infinite loops around regression are detected during automated tests or notified by users and testers.

Attribute routing, which is coming in ASP.NET MVC 5 and Web API 2, offers a smoother way to handle routing. This is especially true when you want to force a particular syntax across all pages and, more likely, all the objects you expose.

The Downside of Classic Routes

A route is a plain parameterized string. You use names in curly brackets to define parameters and concatenate numbers, static text and parameters in a sequence that ultimately determines the URL. A route is always the same thing whether you call it a classic ASP.NET MVC-style route or an attribute route. The difference is all in how you use it and where you define it.

A positive aspect of classic routes is that they are defined in a single place and work the same way regardless of the controller. Overall, you deal with one simple rule: any time a request is placed that matches any of the routes it is picked up. In addition, you can use constraints to restrict values in route parameters. So far so good. Now consider the following common example:

The purpose of the URL is fairly obvious: show me the order with an ID of 1. However, in doing so you are accepting a couple of conventions. First, you must have a class named OrdersController. Second, and more importantly, the OrdersController class must have a public method named Show which accepts an integer. In addition, you must find a way to handle the case in which the integer-the ID-is not provided but for some reasons the resulting URL is matched to the same route.

Among other things, existing conventions open up to the risk of having non-action public methods on controller class. If you happen to have a public method that is not supposed to be callable from the outside you must explicitly opt it out of using the NonAction attribute, otherwise it will be processed anyway, thereby creating a potential security hole.

There’s a way for developers to hide the name of the method from the URL. It requires a custom route handler, as below.

Within a custom route handler you can decide just about everything, including the real name of controller to be used and the action method.

Unfortunately, a custom route handler is just added to the list of routes and you must find the right place for it. It’s no big deal as long as you have 10 routes; it starts getting a problem when you want to exercise a close control over too many individual routes. Here’s the code you need in order to add a route with a custom handler.

Worse yet, when you have a long list of routes, each of which expresses a specific URL template, it is not unusual that you end up with many route handlers-nearly one per route. You understand that this is not particularly satisfactory. In the end, classic routes don’t lack any functionality; they’re simply hard to use when you’re fussy about URL templates. This is quite common in a REST-intensive design. Enter attribute routing.

Attribute Routing in Action

From the technical perspective, attribute routes are as easy as conventional routes except that they work better in a REST design. As their name may suggest, attribute routing is all about having a route attached, as an attribute, to a specific action method.

The code sets the method GetOrderById to be available over a HTTP GET call only if the URL template matches the specified pattern. The route parameter-the orderId token-must match one of the parameters defined in the method’s signature. There are a few more details to be discussed, but the gist of attribute routes is all here.

There’s a clear resemblance with the now old-fashioned WCF Web HTTP programming model and the WebGet attribute in particular.

As I see things, attribute routing is just the revamped and extended version of routing as implemented in the WCF Web HTTP programming model. And, more importantly, it fits a specific requirement that programmers currently have.

Enabling Attribute Routing

Attribute routing is not enabled by default, but it can work side-by-side with conventional routing. Here’s the standard way to enable it:

You don’t have to use a config class with static methods in pure MVC4 style; what really matters is that you invoke the new MapHttpAttributeRoutes method on the HttpConfiguration class. In case you intend to use the two types of routing together, it is preferable you give precedence to attribute routing. This means you call MapHttpAttributeRoutes before you start adding global routes.

You can define a route via attribute for each method you like and also filter on the HTTP method used to call the action. In Web API 2, attribute classes like HttpGet, HttpPost, HttpPut and all others have been extended with an overload that accept a route URL template. If you like it better, you can also use the AcceptVerbs attribute, where the first parameter indicates the method name and the second parameter sets the route.

You can use AcceptVerbs also for unsupported HTTP methods or perhaps WebDAV methods.

Attribute Route Constraints

Attribute routing also supports constraints on parameters using a slightly different syntax than classic routing. The API has a list of predefined constraints such as int, bool, alpha, min, max, length, minlength, range. Here’s how to use them:

The goal of constraints is straightforward. For more details, you can check out some documentation at http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2.

You can concatenate as many constraints as you wish and can create custom constraints as well.

To create a custom constraint you create a class that implements the IHttpRouteConstraint interface and use the following code to register it:

The interface IHttpRouteConstraint has a single method named Match. Finally, you should note that each controller class can be decorated with a route prefix string in order to minimize the route string. When a route prefix is defined it is appended to any attribute routes before parsing. Here’s an example:

You can have multiple route prefixes for a controller; when this happens each route is evaluated individually in the order it appears until a match is found.

Summary

At every new build of ASP.NET MVC 5 and Web API 2, attribute routing gains in importance. It is likely to also end up making a debut in plain ASP.NET MVC. Attribute routing is not the type of feature that alone will push you to use Web API. However, it’s just one of those features that’s really nice to have.