Web API Attribute Routing
Chander Dhall explains how Web API's attribute routing gives developers more control for building and maintaining RESTful APIs using Web API.
November 26, 2013
Related: "Web API: Getting Off the Ground"
ASP.NET Web API relies on the concept of routes to determine mappings of incoming HTTP requests to controllers and actions. Web API does this through a global routing table—as one adds functionality to a project one must become very familiar with WebApiConfig.cs, where the routes are defined. One must constantly update the routing table as the API grows and changes, always keeping in mind that the controllers and actions are located separately from the routing definitions except through convention.
This has some advantages: routes are defined in one place, apply to every controller, and help enforce the conventions. Convention-based routing, however, makes certain scenarios that are common in RESTful APIs hard to support. Additionally, the conventions can result in conflicts in the routing table, matching incorrect actions.
With ASP.NET Web API 2, Microsoft is introducing a new set of routing specification capabilities, dubbed attribute routing, that aim to solve these problems, making it easier to build and maintain RESTful APIs using Web API. These same routing changes are also available to ASP.NET MVC, as MVC and Web API share many of the same technologies.
Related: "Microsoft's Web API Framework: Bridging the Divide Between Web Forms and ASP.NET MVC"
Attribute Routing in a Nutshell
For the sake of simplicity, you'll see dummy implementations where the methods return either default values or assume the actual work is going to be delegated to a different method. Here's an example shows how routes are traditionally defined:
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
Instead, attribute routing lets controllers and actions be decorated with route information. Here's a trivial example:
public class CustomerController : ApiController { [Route("customer/{id}")] public string Get(int id) { return "value"; } }
Here's a more complex example that shows a scenario in which routing would be oblique:
public class BooksController : ApiController { [Route("author/{authorId}/books")] public IEnumerable GetByAuthor(int authodId){ return GetBooksByAuthor(authorId); } [Route("publishers/{publisherId}/books")] public IEnumerable GetByPublisher(int authodId) { return GetBooksByPublisher(authorId); } }
ASP.NET Web API 2 lets both routing techniques be used in concert, with a single configuration change necessary. Here's another example:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "Customer", routeTemplate: "api/{customer}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
Attribute routing exposes a lot of the power of the routing system, closer to where developers will work (controllers, actions), increasing the readability and understanding available in the controllers themselves.
However, this method of routing has its own syntax and complexities, so let's explore in more some detail.
Optional Parameters
To specify an optional parameter in a route attribute, append a '?' to the parameter declaration and define a default:
[Route("books/{page?}")] public IEnumerable GetAll(int page = 0)
Alternatively, the default value can be included in the route definition:
[Route("books/{page=0}")] public IEnumerable GetAll(int page)
A value must be declared for routing to occur correctly.
Constraints
Constraints are declared similarly in the route definition:
[Route("author/{id:int}")] public Author Get(int id){ return new Author(id); } [Route("author/{name}")] public Author Get(string name) { return new Author(name); }
The above lets an author be found by an integer, ID, or name.
Microsoft has detailed available built-in constraints, which are detailed in Table 1:
Constraint Key |
---|
Table 1: Built-in constraints in Web API |
bool |
datetime |
decimal |
double |
float |
guid |
int |
long |
minlength |
maxlength |
length |
min |
max |
range |
alpha |
regex |
Route Prefixing
In a controller with simpler responsibilities, one can adorn a controller with a RoutePrefix attribute, which lets actions be either undecorated specifically or brought into more detail. Here's an example:
[RoutePrefix("author")] public class AuthorController : ApiController { [Route("{id:int}")] public Author Get(int id){ return new Author(); } [Route("{name}")] public Author Get(string name) { return new Author(); } }
Attribute routing, though much awaited, is a great step in the right direction. Traditionally, it was a good idea to have controllers as a part of Web API because the majority of users were MVC developers. This approach reduces the learning curve. However, once you start using Web API, you'll realize that there's no real need for a controller if you have experience in using Node.js, Restify, or even Nancy in .NET.
I think this limitation is easily overcome with attribute routing, and I surely would recommend using this feature wherever applicable.
About the Author
You May Also Like