According to MSDN, Routing is how Web API matches a URI to an action. Web API 2 supports a new type of routing, called attribute routing.
In this article we will look into creating a REST API using Attribute Routing in WEB API2
Introduction
In this article, we will use attribute routing to
create a REST API for a collection of products.For a general overview of attribute routing, see previous articles
Part 1 ,
Part 2,
Objective
The Objective of the article is to create a REST API using Attribute Routing in WEB API 2
- Step 1 We will convert the Productscontroller 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.
[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
//.......
}
Then add [Route] attributes to the controller actions, as follows
[Route("")]
public IQueryable GetProducts()
{
return db.Products.Include(b => b.Manufacturer).Select(AsProductDtO);
}
[Route("{id:int}")]
[ResponseType(typeof(ProductDtO))]
public async Task GetProduct(int id)
{
- Step 2 To get product details, the client will send a GET request to /api/products/{id}/details, where {id} is the ID of the product,
Add the following method to the ProductsController class.
[Route("{id:int}/details")]
[ResponseType(typeof(ProductDetailDto))]
public async Task<IHttpActionResult> GetProductDetail(int id)
{
var product = await (from b in db.Products.Include(b => b.Manufacturer)
where b.ManufacturerId == id
select new ProductDetailDto
{
Title = b.Title,
Category = b.Category,
ReleaseDate = b.ReleaseDate,
Price = b.Price,
Description = b.Description,
Manufacturer = b.Manufacturer.Name
}).FirstOrDefaultAsync();
if (product == null)
{
return NotFound();
}
return Ok(product);
}
Now If we request /api/products/1/details, the response looks like this

- Step 3 : To get a list of products in a specific category,the client will send a GET request to /api/products/category, where category is the name of the category. (For example, /get/products/Electronics.).
Add the following method to ProductsController.
[Route("{genre}")]
public IQueryable<ProductDtO> GetProductsByCategory(string category)
{
return db.Products.Include(b => b.Manufacturer)
.Where(b => b.Category.Equals(category, StringComparison.OrdinalIgnoreCase))
.Select(AsProductDtO);
}
Here we are defining a route that contains a {category} parameter in the URI template. Notice that Web API is able to distinguish these two URIs and route them to different methods:
/api/products/1
/api/products/Electronics
That's because the GetProduct method includes a constraint that the "id" segment must be an integer value:
Here is the complete code for the ProductsController class
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using ProductsAPI.Models;
using ProductsAPI.DTOs;
using System.Linq.Expressions;
namespace ProductsAPI.Controllers
{
[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
private ProductsAPIContext db = new ProductsAPIContext();
// Typed lambda expression for Select() method.
private static readonly Expression<Func<Product, ProductDtO>> AsProductDtO =
x => new ProductDtO
{
Title = x.Title,
Manufacturer = x.Manufacturer.Name,
Category = x.Category
};
// GET api/Products
[Route("")]
public IQueryable<ProductDtO> GetProducts()
{
return db.Products.Include(b => b.Manufacturer).Select(AsProductDtO);
}
// GET api/Products/5
[Route("{id:int}")]
[ResponseType(typeof(ProductDtO))]
public async Task<IHttpActionResult> GetProduct(int id)
{
ProductDtO product = await db.Products.Include(b => b.Category)
.Where(b => b.ProductId == id)
.Select(AsProductDtO)
.FirstOrDefaultAsync();
if (product == null)
{
return NotFound();
}
return Ok(product);
}
[Route("{id:int}/details")]
[ResponseType(typeof(ProductDetailDto))]
public async Task<IHttpActionResult> GetProductDetail(int id)
{
var product = await (from b in db.Products.Include(b => b.Manufacturer)
where b.ManufacturerId == id
select new ProductDetailDto
{
Title = b.Title,
Category = b.Category,
ReleaseDate = b.ReleaseDate,
Price = b.Price,
Description = b.Description,
Manufacturer = b.Manufacturer.Name
}).FirstOrDefaultAsync();
if (product == null)
{
return NotFound();
}
return Ok(product);
}
[Route("{genre}")]
public IQueryable<ProductDtO> GetProductsByCategory(string category)
{
return db.Products.Include(b => b.Manufacturer)
.Where(b => b.Category.Equals(category, StringComparison.OrdinalIgnoreCase))
.Select(AsProductDtO);
}
//To get a list of a products for a particular manufacturer, the client will send a GET request to /api/manufacturers/id/products, where id is the ID of the manufacturer.
[Route("~api/manufacturers/{manufacturerId}/products")]
public IQueryable<ProductDtO> GetproductsByManufacturer(int manufacturerId)
{
return db.Products.Include(b => b.Manufacturer)
.Where(b => b.ManufacturerId == manufacturerId)
.Select(AsProductDtO);
}
//Here we restricted the route to a particular format by adding a regular-expression constraint to the route template
[Route("date/{reldate:datetime:regex(\\d{4}-\\d{2}-\\d{2})}")]
[Route("date/{*reldate:datetime:regex(\\d{4}/\\d{2}/\\d{2})}")]
public IQueryable<ProductDtO> GetProducts(DateTime pubdate)
{
return db.Products.Include(b => b.Manufacturer)
.Where(b => DbFunctions.TruncateTime(b.ReleaseDate)
== DbFunctions.TruncateTime(pubdate))
.Select(AsProductDtO);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
you can get the complete source code form
here
Conclusion
In this article we have seen how to create a REST API using Attribute Routing
Reference
http://www.asp.net/web-api/overview/web-api-routing-and-actions/create-a-rest-api-with-attribute-routing