Attribute based routing in ASP.NET Web API - StrathWeb

Strath

Attribute based routing in ASP.NET Web API

Easy way to have granular control over your routes

Routing is the key aspect of every MVC application – after all, it’s how people get to your application, and how search engines see it. As flexible as routing in ASP.NET MVC has been, one would often end up in frustrating situations where more flexibility was needed (or you simply started getting lost in a maze of routes). One of the finest extensions to ASP.NET MVC I have ever worked with is the excellent library AttributeRouting by Tim McCall.

Last month, through the great work of Kamran Ayub, the library has been extended to support ASP.NET Web API, and is now available on NuGet. Let’s have a look at how it can immediately make your life easier and drastically improve the way you handle your routes.

Getting the appropriate stuff from NuGet

You need to start off with a Web API project, and grab the AttributeRouting for Web API from NuGet. The package name is AttributeRouting.WebAPI and it is dependent on the WebActivator.

Note: If you want to use AttributeRouting for self-hosted Web API, grab the AttributeRouting.WebAPI.Hosted package instead.

What just happened

You’ll notice that upon installation from NuGet, a new folder appeared in your app, called App_Start, which contains a static class, AttributeRoutingHttp. This is how, through WebActivator AttributeRouting plugs itself automatically into the application start pipeline. If you don’t like this setup (like me) you can move the code to Global.asax.

In here, your controllers are scanned for the routing attributes, and the plumbing happens – the routes are generated based on the attributes and registered in your application.

Using the attributes

The concept is wonderfully simple, just decorate your controllers and their actions with routing attributes, defining all kinds of route-specific information:
– route names
– access method/verb
– route constraints
– default and optional parameters
– route areas
– area mapping
– route prefixes
– route translations
– routing conventions

and many more.

Since you do all that at such a low level (action or controller), you get the ultimate, granular control of routing in your application.

To be able to get started, first import the appropriate namespaces to your controllers – for ASP.NET Web API these are:

Let’s go through a few examples. For the record, I’ll be using the same simple repository that I used in this blog post.

Example – Basic HTTP methods

Let’s add a controller and a simple get all action.

Now this can be accessed easily:
/links/ (via GET)

We can add more routes to same action, we can also mix the verbs

This action can now be invoked:
/links/ (via GET)
/urls/ (via GET)
/postedlinks/ (via POST)

We can easily pass paramters:

The single item can now be requested like this:
/url/1 (via GET)

Example – route constraints

We can also very easily add route constraints. In fact, through a terrific work of Xavier Poinas, there is a special syntax for that.

Let’s take the example from previous paragraph, and limit a range in which the ID can be used.

This will now respond to the following example:
/url/1 (via GET)
but not to
/url/4 (via GET)

Routing constraints can , among there many terrific options, also take in regular expressions

This is a simple example, with the pattern [large letter] [small letter] [number]. So this action will respond to requests which follow such format only, i.e.:
/text/Ab1 (via GET)

As mentioned, there are many more constraints possible. All are listed here.

Example – default and optional parameters

You can define the default and optional paramteres for the route as well. Let’s use the previous example.

Now, if you request
/url/ (via GET)
you’d actually get the same content as in
/url/1 (via GET)
as the route parameter id would default to 1.

You can also set up optional parameters:

This can be requested:
/optionaltext/ (via GET)
/optionaltext/hi (via GET)
/optionaltext/hi/hello (via GET)

Example – route prefixing

Another great functionality of AttributeRouting is the ability to prefix all the routes withing controller with a common prefix.

In this case the entire controller is prefixed by items. This means we access the above action like this:
/items/ (via GET)
/items/links/ (via GET)

Example – easy translations

AttributeRouting provides a really easy way for supporting transations. Just decorate your action methods with appropriate key, and in the Application_Start read the key values for a given language. Then initialize the routes by passing the translations in the config.

That will automatically generate translated routes for you. (Note, the example has translations hardcoded, normally you’d read them from resx or XML or DB).

Please note that you need to specify to which Controller the translations need to be applied.

And next, the attributes declaration:

You can now access both the default, german and french routes:
/items/ (via GET)
/german-items/ (via GET)
/french-items/ (via GET)
/items/links/ (via GET)
/german-items/german-links/ (via GET)
/french-items/french-links/ (via GET)

Summary

These were just a handful of examples, but hopefully it was enough to draw you interest to the wonderful AttributeRouting library.

I have to admit, I’ve been using it for MVC for quite a while and I think it’s one of the best thing that could have happened for MVC developers. Since now it is available for ASP.NET Web API I guarantee you that after using it for a while you’ll never want to go back to the dark ages of declaring routes in a “traditional” way :-)

Cheers!

Be Sociable, Share!

  • Emily Parker

    Very informative for the budding developers. Hope they make good use of these tips.

  • Pingback: Dew Drop – May 24, 2012 (#1,334) | Alvin Ashcraft's Morning Dew

  • http://www.swoo.co.uk Steve Woods

    I like the concept, but it looks (to me) like a maintenance nightmare, especially if your URLs change, unless i’m missing something (I probably am).

  • jmvermeulen

    Will this be compatible with WebForms too?

    • http://kamranicus.com Kamran Ayub

      Do you mean web forms in general or Web API with web forms? If the latter, yes, if the former, no because it relies on routing and decorating action methods.

      • jmvermeulen

        Ok thanks, indeed I meant this project with Web Forms.

  • http://kamranicus.com Kamran Ayub

    Thanks Filip for writing about this! I loved using AR in MVC so I couldn’t live without it in Web API.

    Steve, if you like customizing URLs more than using the default route template, I’ve found the Global.asax method too cumbersome and not easily discoverable or maintainable. I really like AR because URLs are nearby where they’re used.

    Using route prefixing and route areas, you can reduce maintenance but you’re right: the more URLs you customize, the more you need to go back and modify them if they change. For myself, I haven’t found it to be an issue for a small site. Since it’s an attribute, I suppose you could store them in constants if you really wanted.

  • Mike Vanderkley

    I have been using this for all my projects and I cannot live without it.

  • Sara Brooks

    We have been extensively using AttributeRouting for Web API and it is very beneficial in our ASP.NET MVC applications.

    Sara Brooks,
    http://www.hiredotnetprogrammers.com

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1113

  • http://timjames.me Tim James

    Well written post.
    I can see the translations coming in handy in a project which I will be working on soon.

  • Pingback: Distributed Weekly 156 — Scott Banwart's Blog

  • link

    Very nice article, just what I was looking for.

  • http://leniel.net Leniel Macaferi

    AttributeRouting also has an awesome feature to generate Lowercase Outbound URLs:

    https://github.com/mccalltd/AttributeRouting/wiki/Generating-Lowercase-Outbound-URLs

  • http://www.kodefuguru.com Chris Eargle

    This is great stuff! I suppose what I like about this is that I can provide default routes through the attributes, and if someone else takes the project (say it’s OSS), they can simply provide their own routes in the manner in which they are accustomed.

  • Pingback: 5/25/2012–News of the Day | Bryan Hinton on Tech and Business and …

  • link

    I am actually grateful.

  • Mike

    Have you thought about trying to submit a pull request, to get this into MVC? They are SUPPOSED to be open to this now, but we’ll see if anybody else’s code actually makes it into the MVC stack, or if it’s just for show. :)

    • Filip W

      haha well, I’m not in a position to do that :)
      as mentioned in the post, this is all Kamran’s (www.kamranicus.com) library so you should ask him. However, I love it and it definitely should be part of the core (imho). I think it has been discussed by ASP.NET Team at some point, but then they dropped the idea.

  • Pingback: friday link 32 « A Programmer with Microsoft tools

  • Sarmaad

    Awesome tool… Does it work with normal MVC controllers 4.0 or 3.0?

    I am trying it and its not working as expected in an MVC 4.0, but fine in WebAPI controller.

    Thanks.

  • brad laney

    Odd. Does this not work in integrated mode? After installing I get “An ASP.NET setting has been detected that does not apply in Integrated managed pipeline mode.”

    • Dejan Radovic

      Got the same issue. Did you ever resolve it?

  • brad laney

    I don’t know if this is expected behavior but it doesn’t seem like it to me. It just destroyed any hopes I had of using it.

    [RoutePrefix("bid")]
    [GET("{id}/activation")]
    public TestModel Activation(int id)
    “The requested resource does not support http method ‘GET’.”

  • Eddie Falcione

    Hi there ,have been searching for this info for sometime now and finally found it. Good work and great looking site. Thank you

  • Isaiah Gary

    Hi there ,have been searching for this info for sometime now and finally found it. Good work and great looking site. Thank you

  • Bogdan Zamfir

    Hi,
    Thanks for this very interesting package. It seems to be what I am looking for.
    However, I have poblems getting it to run

    This is what I did:

    1. in Application_Start I have this code

    AttributeRoutingHttp.RegisterRoutes(RouteTable.Routes);
    RouteConfig.RegisterRoutes(RouteTable.Routes);

    2. in RouteConfig.RegisterRoutes I register webapi routes as below:

    routes.MapHttpRoute(
    name: RestHelper.DefaultApiRoute,
    routeTemplate: “api/{controller}/{id}”,
    defaults: new { id = RouteParameter.Optional }
    );

    And I have a controller, as below

    public class DeviceController : ApiController
    {

    [GET("device/all")]
    public IQueryable Get()
    {
    return GetAlldevies();
    }

    public DeviceDTO Get(string id)
    {
    retun GetDevice(id);
    }

    When I try the url

    http://myserver/api/device/all

    the code gets into Get(string id) instead of Get()

    I also tried to set the get attribute as
    [GET("devices")]

    but when I try to access with url

    http://myserver/api/devices

    I get answer that there is no Devices controller

    Any idea what am I doing wrong?

    Thanks

  • Luke

    Brad you just need to remove the section that the nuget package drops in the web.config. Integrated mode has its own section for httphandlers under .

    Hope this helps.

  • http://ProblemwithGET ChrisMoses

    This. is cool. Really cool! They should add this to the official release.

    I do, however, have a small problem. Not sure what I am doing wrong. Any poiters would eb greatly appreciated:
    I cant get any “GET” routes to work:
    [POST("Children/{id}")] // <- Works fine
    [GET("Children/{id}")] // <-Does not work

    The GET simply returns:
    "The requested resource does not support http method 'GET'."

    Anybody have ANY thoughts??

  • Eric Nathaniel Wilson

    Magnificent! (As usual. :-P )

  • Pingback: WebAPI | Pearltrees

  • Pingback: List of ASP.NET Web API and HttpClient Samples - .NET Web Development and Tools Blog - Site Home - MSDN Blogs

  • George

    Thanks very much, is it possible to attache a simple example as its conflicting with the Webapi built-in verbs.

  • George

    Hi Filip, me George again. The [POST("postedlinks")] does not work by its own unless you decorate the action method with [HttpPost], right.
    Thanks.

  • Gadi B

    can’t get the optional parameters to work. i’ve tried using the question mark before the param name or after it. still it is treated as mandatory. any ideas?
    Thanks.

  • Gadi B

    Can’t get the optional parameters to work. I’ve tried using the “?” before and after the param name. any ideas?
    Thanks.

  • Pingback: The Origin of RESTful URLs | Wizards of Smart