Attribute Routes and Hierarchical Routing

Monday, August 12, 2013

As announced earlier this year, attribute routing will be a part of the next ASP.NET release. You can read more about the feature on the asp.net wiki, but here is a quick example to get the idea:   

public class ServerController : ApiController
{         
    [GET("api/server/{server}")]
    public IEnumerable<string> GetDatabaseNames(string server)
    {
        // ...
    }

    [GET("api/server/{server}/{database}")]
    public IEnumerable<string> GetCollectionNames(string server, string database)
    {
        // ...        
    }     
}

Attribute routing has been around for some time (you can use it today), and is invaluable for certain types of applications. While today’s default routing approach in ASP.NET MVC and Web API is easy and conventional, the  approach lacks the flexibility to make the hard scenarios easy. Scenarios like modeling hierarchical resources as in the above code, where the application wants to respond to api/server/localhost/ and api/server/localhost/accoutingdb. Other scenarios include creating controller actions with custom parameter names, and actions that can respond to multiple URIs.

Overall, the addition of attribute routing to the framework is a win.

However . . .

One of the benefits of attribute routing listed on the asp.net wiki is:

[Talking about conventional routing] –> The information about what URI to use to call into a controller is kept in a completely different file from the controller itself. A developer has to look at both the controller and the global route table in configuration to understand how to call into the controller.

An attribute-based approach solves all these problems by allowing you to configure how an action gets called right on the action itself. For most cases, this should improve usability and make Web APIs simpler to build and maintain.

I disagree!

I personally like centralized route configuration. Having routes defined in one place makes it easier to order and optimize the routes, and also think about URIs before thinking about an implementation (which is arguably more important for an API than a regular web site).

As a comparison, consider Darrel Miller’s ApiRouter, which also allows for flexibility and hierarchy in the routing rules (below is an excerpt for routing rules to mimic GitHub’s model). 

Add("issues", ri=> ri.To<IssuesController>());
Add("events", ri => ri.To<EventsController>());
Add("networks", 
  rn => rn.Add("{userid}", 
    ru => ru.Add("{repoid}", 
      rr => rr.To<NetworksController>())));

Add("gists",rg => rg.To<GistsController>()
  .Add("public",
     rp => rp.To<GistsController>(new { gistfilter = "public" }))
  .Add("starred", 
     rs => rs.To<GistsController>(new { gistfilter = "starred" }))
  .Add("{gistid}", 
      rgi => rgi.To<GistController>("justid")
  .Add("comments", 
      rc => rc.To<GistCommentsController>())

In the end, I believe using an approach like ApiRouter will lead to a routing configuration that is easier to understand, optimize, maintain, and troubleshoot.

I believe it will also will lead to a better API design, because attribute routing makes it easy to destroy the uniform interface for a resource and avoid looking at the bigger picture of how the URIs work together. 

Thoughts?


Comments
gravatar roysvork Monday, August 12, 2013
Some people like the idea of defining routes near to where they are used, and others like to have a single point of definition (a la IOC container bootstrappers). I'm in the latter camp and I'm glad to hear that someone agrees with me! You may be interested in a project that I am working on, Superscribe which extends this concept of hierarchical routing even further, introducing the concept of interpreting routes as a State Machine: There is some documentation on GitHub (https://github.com/Roysvork/Superscribe), and I am hoping to do some talks on the subject. It is my opinion that routing is not given high enough status in web apps, and with the advent of OWIN\Katana I believe it could\should become seperated from web frameworks themselves, even transcend them. One point of definition for routes, even maybe with customisable OWIN pipelines for each one. Would love to discuss this some more!
gravatar richard reukema Monday, August 12, 2013
Totalllyagree. Designers are not thinking clearly about there api early enough and fall into the trap of functional decomposition for their design. Have a look at all your routesin one place would Help. I have to admit though that global route table has confused me before so better debug, or perhaps a Visual rout diagram? Like crating the model from code first in in ef?
gravatar Phillip Haydon Monday, August 12, 2013
The problem with this is routes are once again hidden from their implementation and creates a maintenance nightmare. The best thing about AR is it doesn't hide the routes. Personally I think apirouter is just sugar syntax on routing. AR solves the actual problem of routing. Routing in web API and mvc was crap because its order dependent. Because a single route is magic for all calls. This isn't the case with AR.
gravatar Bryan Johns Monday, August 12, 2013
Why not both? Most, if not all, IOC containers allow for configuration in app/web.config or in code. Why not allow the route table be built the same way allowing devs to choose their preferred method?
gravatar WP Tuesday, August 13, 2013
Why not use NancyFX ? :)
gravatar Darrel Miller Tuesday, August 13, 2013
One of the reasons I created the hierarchical routing was to make it easier to break the route configuration up into whatever size of chunks you like. If you want it all in one place, cool. If you want every controller to have something like a static BuildRouter method then that works too. So the top level code for the sample could be, Add(IssuesController.BuildRouter()); Add(EventsController.BuildRouter()); Add(NetworksController.BuildRouter()); Add(GistsController.BuildRouter()); and the other details can be pushed down into the parent controllers. Both are valid options. Personally I believe when an API gets very large (> 500 controllers) then dividing the routing becomes the easiest way to handle it and using a hierarchy makes it easier to break it up than standard MVC routing.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!