Web API 2.0 – Attribute Routing

Web API routing has been pretty flexible using the convention based routing and custom Route Templates. We could specify custom routes, but things got verbose and disconnected for situations where we had to define routes for sub-resources, like for example if you had a list of Books and each book had more than one author, to fetch the authors for a book, you might want a route like http://myapp/api/books/{id}/authors

To do this, we had to define a route in the Route Table as follows

config.Routes.MapHttpRoute(
    name: “AuthorsForBook”,
    routeTemplate: “api/books/{id}/authors”,
    defaults: new { controller = “books”, action=“GetBookAuthors” }
);


Now this works but if we wanted another one, say for a list of Authors, select an author and then the books they have authored, the URL would be http://myapp/api/authors/{id}/books

This would mean one more route added to the route table in the WebApiConfig.cs file. However the implementation would be in the Controller class thus resulting in a disconnect between the Resource (API) and the Route Declaration.

However if we could declare the routes as attributes on top of the Api Controller’s action methods themselves, this disconnect would go away. Thanks to an active Community Member Tim McCall (of http://attributerouting.net fame), we got Attribute based routing, that among other things, allowed us to define routes as Attributes instead of adding them to a routing table manually in a config file. Since Web API is open Source, the Microsoft team reached out to Tim and it was mutually agreed to include it as a part of Core Web API and that’s how Web API 2.0 got it’s Attribute Routing feature.

Thanks to Sumit Maitra for implementing the code for this article

Hands on with a Web API 2.0 (Beta) sample

With the premise and history of Attribute Routing out of the way, let’s jump into some code.

Today we are using Visual Studio 2013 Preview (Ultimate) because it also gives a glimpse of the upcoming features of Visual Studio and the bigger One ASP.NET philosophy that the ASP.NET team has been driving towards for a while.

Step 1: Fire up Visual Studio and from the New Project dialog, select ‘Web’ templates on the Left hand navigation and pick ASP.NET Web Application. Notice the lack of ASP.NET WebForms/ASP.NET MVC/Dynamic Data etc. etc. templates, only One ‘ASP.NET Web Application’ template. We provide a name and continue.

visual-studio-new-project-dialog

Step 2: Here we are greeted with a dialog new in Visual Studio 2013’s web tooling. It allows us to pick Web API, Web Forms, MVC, SPA, Facebook, Mobile etc.

web-api-project-type

Note the intent of One ASP.NET, there is a mix match capability, for example in the above scenario, we have selected Web API and MVC is checked by default, but we can have Web Forms in the Mix too. Similarly we can pick Web Forms by default and then add MVC to the mix. For MVC and Web Forms, the Configure Authentication button is enabled and provides further options for setting up Authentication. Without diverging too much at this point, let’s click Create Project and go ahead.
The Solution Explorer is not much different from the current Web API template

new-web-api-solution

Step 3: We add two entities Author and Book

public class Author
{
    public int AuthorId { get; set; }
    public string Name { get; set; }
}

public class Book
{
    public int BookId { get; set; }
    public string Title { get; set; }
    public List<Author> Authors { get; set; }
}

Step 4: Ideally I wanted to add these to the DB to create a realistic Demo but unfortunately my Visual Studio 2013 Preview refused to Scaffold the entities using Entity Framework, so instead I scaffolded a controller with read/write actions only.

Step 5: I updated the Get methods and added GetAuthorBooks and GetBookAuthors methods. These were then decorated with route attributes as follows

public IEnumerable<Author> Get()
{
return new List<Author> { new Author { AuthorId = 1, Name = "Suprotim" }, new Author { AuthorId = 2, Name = "Sumit" } };
}


// GET api/authors/5
public Author Get(int id)
{
if (id == 1)
{
  return new Author { AuthorId = 1, Name = "Suprotim" };
}
else
{
  return new Author { AuthorId = 2, Name = "Sumit" };
}
}


[HttpGet("api/authors/{authorId}/books")]
public IEnumerable<Book> GetAuthorBooks(int authorId)
{
Book newBook = null;
if (authorId == 1)
{
  newBook = new Book { BookId = 1, Title = "51 Recipes in jQuery" };
}
else
{
  newBook = new Book { BookId = 2, Title = "Windows 8 Apps" };
}
return new List<Book> { newBook };
}


[HttpGet("api/books/{bookId}/authors")]
public IEnumerable<Author> GetBookAuthors(int bookId)
{           
return new List<Author>();
}

Step 6: Setting up Test Harness. By default Web API projects come with HelpPage harness that auto generates API documentation. Now if we add the following Nuget Package, this drops another component that generates UI for posting data to Web API services.

PM> install-package WebApiTestClient

The WebApiTestClient needs Knockout and jQuery UI as well. These were not installed in my version of Visual Studio 2013 Preview, so I installed them separately using the following commands in the Nuget Package Management Console

PM> install-package knockoutjs
PM> install-package jquery.ui.combined


Once installed, open Areas\HelpPage\Views\Help\Api.cshtml and add this towards the bottom of the page

@Html.DisplayForModel("TestClientDialogs")

Inside the Scripts Section, add the following

@Html.DisplayForModel("TestClientReferences")

Now we are all set to test our Attribute Routing.

Step 7: Run the application! Voila BootStrap styles out of the box.

mvc-default-bootstrap-style

Click on API (in the header) to navigate to the Help Pages (yes, they look gross because BootStrap styling haven’t made it in there yet).

As we can see, APIs for our new ApiController documentation is ready for us.

web-api-help-page-home

Step 8: Click on the API link for the GetAuthorBooks

author-books-help-page

Note the depth of details including response body formats in JSON (and XML if you scroll down).
Step 9: Click on the ‘Test API’ button. This will give you a jQuery dialog with space to input URI parameters

author-books-test-input-ui

Step 10: Put 1 as {authorId}. Hit Send.

At the backend, if we have a breakpoint, we’ll see that we received the parameter correctly:

author-books-api-breakpoint

When we continue, we get the following response in our UI

author-books-response-ui

As we can see the Status is a 200, and the Body has the array of books that this author has written (which as we saw is hardcoded).

We can tryout the GetBookAuthors API similarly as well.

Essentially we were able to manipulate the Routes via Attributes instead of manually adding routes in Route Tables. Thing to note is Attribute routes are not magic and they do get added to the Route table, it’s just that applying routes via Attributes makes things more coherent and compact from an API declaration point of view.

Other Route Attribute Features

Apart from above routing feature, Route Attributes can enforce the following

Default parameters: The following route implies default authorId is 99

[HttpGet("api/authors/{authorId=99} ")]
public Author Get(int authorId)


Optional Route Parameters: Similarly the following syntax implies optional parameters

[HttpGet("api/authors/{authorId?} ")]
public Author Get(int authorId)



Constraining Route Types: Route constraints can also be specified and we can set constraints based on data type/length/min/max etc. The following sample shows Integer data type constraint.

[HttpGet("api/authors/{authorId:int} ")]
public Author Get(int authorId)


Conclusion

The ASP.NET Web Tooling team is hard at work bringing the new One ASP.NET vision to reality. In the meantime, Web API 2.0 is coming with some cool new features. Today we saw only one of these i.e. AttributeRouting. As the releases stabilizes, we will see more of the new stuff in Web API 2.0.

Note: If you want AttributeRouting NOW (in MVC and WebAPI), use the Nuget Package created by Tim McCall, for details refer to his site http://attributerouting.net/

Download the source code of this article (Github)




About The Author

Suprotim Agarwal
Suprotim Agarwal, Developer Technologies MVP (Microsoft Most Valuable Professional) is the founder and contributor for DevCurry, DotNetCurry and SQLServerCurry. He is the Chief Editor of a Developer Magazine called DNC Magazine. He has also authored two Books - 51 Recipes using jQuery with ASP.NET Controls. and The Absolutely Awesome jQuery CookBook.

Follow him on twitter @suprotimagarwal.

No comments: