Web API 2's Attribute Routing: Looking Deeper

  • Nov 12, 2013 at 1:38 PM
  • Shawn Wildermuth
  • 7 Comments

underwaterI recently recorded a new module for ASP.NET  Web API course (on Pluralsight) to cover the new features of Web API 2 (it’s not out yet, soon…I promise).

It was fun plumbing the depths of how it works. I generally like the feature but the implementation may change some of your code if you’re dealing with routes under the covers (i.e. for auth or versioning).

If you want a quick overview, I really like Dan Wahlin’s walkthrough of the feature here:

http://weblogs.asp.net/dwahlin/archive/2013/11/11/new-features-in-asp-net-web-api-2-part-i.aspx

While most of us are used to creating routes using the Web API configuration, attributed routes are different. Before attributed routing, when you would look at the route data (e.g. request.GetRouteData()) you would get a route with a name that tied to the configured name. This was really useful for the way to use UrlHelper to build your URLs.

In fact, you can get this behavior by supplying names to the individual routes in your attributed routing:

public FooController : ApiController
{
  [Route("api/foos", Name = "Foo")]
  public object Get()
  {
    // ...
  }
}

...

var helper = new UrlHelper(Request);
var url = helper.Link("Foo");

But if you’re traversing the route collection in any way (e.g. in a ControllerSelector) it is important to understand where these routes actually are. When you use attribute routing, all the route attributes get added to a common route without a name. This is a special route that is an instance of an internal class called RouteCollectionRoute (Source Link). This route has a collection of sub-routes that you can query for that includes *all* the attribute routes. But if you just want the selected route for your call, you can simple ask for it using the RouteData.Values:

var routeData = request.GetRouteData();
var subroutes = (IEnumerable<IHttpRouteData>)routeData.Values["MS_SubRoutes"];
var route = subroutes.First().Route;

The real problem for some is that there is no longer controller name in the route data. This makes sense of course because there is no specific controller that a route points to as attributed routes are related to methods, not controllers. Being aware of these internals may help you solve issues when you use or move to using attributed routes.

Hope this helps!

 

Comments

Gravatar

Rune Monday, January 20, 2014

Hi,

I've watched your great "Implementing ASP.NET Web API" course on PluralSight.
Do you know of any code samples where versioning (not url) and attributed routing is being used?

What is your personal perferences? Do you use the old routing style or are you using attributed routing when versioning is required?

Thanks,
Rune

Gravatar

Shawn Wildermuth Monday, January 20, 2014

I use attributed routes for exceptions and standard routing for main cases.

Gravatar

Alex Tercete Friday, January 31, 2014

Great!

I replaced
```
var subroutes = (IEnumerable<IHttpRouteData>)routeData.Values["MS_SubRoutes"];
```
with
```
var subroutes = routeData.GetSubRoutes(); // extension method available in System.Web.Http.Routing
```
to avoid dealing with "magic constants".

Gravatar

Anthony Steele Monday, March 10, 2014

What I found was that when the controller has Get and Post methods, for the same route but different http verbs, the subroutes contains two routes, and I have no obvious way to tell which one I have actually matched. One of them has the right action name, the other does not.

Gravatar

Pankaj Sharma Monday, April 28, 2014

Is there a way to implement Web API Versioning along with Attribute Routing. For some reasoning Attribute Routing does not work with Versioning like v1/products, v2/product

Gravatar

Shyam Tuesday, September 30, 2014

Hi Shawn,
I follow all your courses on plural sight and find it easy to understand. Thanks for those courses. I have one question on the sub routes of RouteData. When the sub routes collection has got more than one item, how do we know that first is always the correct and best match? Is there any ranking or ordering that happens internally before adding the matches to the collection? Please let me know your thoughts.

var route = subroutes.First().Route;

I am referring to the above code.

Thanks,
Shyam


Leave a Comment

*
*
*