Create a REST API with Attribute Routing in ASP.NET Web API 2
====================
by [Mike Wasson](https://github.com/MikeWasson)
Web API 2 supports a new type of routing, called *attribute routing*. For a general overview of attribute routing, see [Attribute Routing in Web API 2](attribute-routing-in-web-api-2.md). In this tutorial, you will use attribute routing to create a REST API for a collection of books. The API will support the following actions:
| Action | Example URI |
| --- | --- |
| Get a list of all books. | /api/books |
| Get a book by ID. | /api/books/1 |
| Get the details of a book. | /api/books/1/details |
| Get a list of books by genre. | /api/books/fantasy |
| Get a list of books by publication date. | /api/books/date/2013-02-16 /api/books/date/2013/02/16 (alternate form) |
| Get a list of books by a particular author. | /api/authors/1/books |
All methods are read-only (HTTP GET requests).
For the data layer, we'll use Entity Framework. Book records will have the following fields:
- ID
- Title
- Genre
- Publication date
- Price
- Description
- AuthorID (foreign key to an Authors table)
For most requests, however, the API will return a subset of this data (title, author, and genre). To get the complete record, the client requests `/api/books/{id}/details`.
Start by running Visual Studio. From the **File** menu, select **New** and then select **Project**.
In the **Templates** pane, select **Installed Templates** and expand the **Visual C#** node. Under **Visual C#**, select **Web**. In the list of project templates, select **ASP.NET MVC 4 Web Application**. Name the project "BooksAPI".
In the **New ASP.NET Project** dialog, select the **Empty** template. Under "Add folders and core references for", select the **Web API** checkbox. Click **Create Project**.
This creates a skeleton project that is configured for Web API functionality.
### Domain Models
Next, add classes for domain models. In Solution Explorer, right-click the Models folder. Select **Add**, then select **Class**. Name the class `Author`.
In this step, we'll add a Web API controller that uses Entity Framework as the data layer.
Press CTRL+SHIFT+B to build the project. Entity Framework uses reflection to discover the properties of the models, so it requires a compiled assembly to create the database schema.
In Solution Explorer, right-click the Controllers folder. Select **Add**, then select **Controller**.
In the **Add Controller** dialog, for **Controller name**, enter "BooksController". Select the "Use async controller actions" checkbox. For **Model class**, select "Book". (If you don't see the `Book` class listed in the dropdown, make sure that you built the project.) Then click the "+" button.
Click **Add** in the **Add Controller** dialog. The scaffolding adds a class named `BooksController` that defines the API controller. It also adds a class named `BooksAPIContext` in the Models folder, which defines the data context for Entity Framework.
This command creates a Migrations folder and adds a new code file named Configuration.cs. Open this file and add the following code to the `Configuration.Seed` method.
If you run the application now and send a GET request to /api/books/1, the response looks similar to the following. (I added indentation for readability.)
Instead, I want this request to return a subset of the fields. Also, I want it to return the author's name, rather than the author ID. To accomplish this, we'll modify the controller methods to return a *data transfer object* (DTO) instead of the EF model. A DTO is an object that is designed only to carry data.
In Solution Explorer, right-click the project and select **Add** | **New Folder**. Name the folder "DTOs". Add a class named `BookDto` to the DTOs folder, with the following definition:
Next, update the `BooksController` class to return `BookDto` instances. We'll use the [Queryable.Select](https://msdn.microsoft.com/en-us/library/system.linq.queryable.select.aspx) method to project `Book` instances to `BookDto` instances. Here is the updated code for the controller class.
Next, we'll convert the controller to use attribute routing. First, add a **RoutePrefix** attribute to the controller. This attribute defines the initial URI segments for all methods on this controller.
The route template for each controller method is the prefix plus the string specified in the **Route** attribute. For the `GetBook` method, the route template includes the parameterized string "{id:int}", which matches if the URI segment contains an integer value.
To get a list of books in a specific genre, the client will send a GET request to `/api/books/genre`, where *genre* is the name of the genre. (For example, `/get/books/fantasy`.)
Here we are defining a route that contains a {genre} parameter in the URI template. Notice that Web API is able to distinguish these two URIs and route them to different methods:
`/api/books/1`
`/api/books/fantasy`
That's because the `GetBook` method includes a constraint that the "id" segment must be an integer value:
This example is interesting because "books" is treated a child resource of "authors". This pattern is quite common in RESTful APIs.
The tilde (~) in the route template overrides the route prefix in the **RoutePrefix** attribute.
## Get Books By Publication Date
To get a list of books by publication date, the client will send a GET request to `/api/books/date/yyyy-mm-dd`, where *yyyy-mm-dd* is the date.
The `{pubdate:datetime}` parameter is constrained to match a **DateTime** value. This works, but it's actually more permissive than we'd like. For example, these URIs will also match the route:
`/api/books/date/Thu, 01 May 2008`
`/api/books/date/2000-12-16T00:00:00`
There's nothing wrong with allowing these URIs. However, you can restrict the route to a particular format by adding a regular-expression constraint to the route template:
Now only dates in the form "yyyy-mm-dd" will match. Notice that we don't use the regex to validate that we got a real date. That is handled when Web API tries to convert the URI segment into a **DateTime** instance. An invalid date such as '2012-47-99' will fail to be converted, and the client will get a 404 error.
You can also support a slash separator (`/api/books/date/yyyy/mm/dd`) by adding another **[Route]** attribute with a different regex.
This tells the routing engine that {pubdate} should match the rest of the URI. By default, a template parameter matches a single URI segment. In this case, we want {pubdate} to span several URI segments:
`/api/books/date/2013/06/17`
## Controller Code
Here is the complete code for the BooksController class.