New Features in ASP.NET Web API 2 | Web API Tutorial

,

Attribute Routing

Routing has been a core feature of Web API since it’s initial release and something that’s built into new Web API projects out-of-the-box. However, there are a few scenarios where defining routes can be challenging such as nested routes (more on that in a moment) and any situation where a lot of custom routes have to be defined. For this example, let’s assume that you’d like to define the following nested route:

/customers/1/orders

This type of route would select a customer with an Id of 1 and then return all of their orders. Defining this type of route in the standard WebApiConfig class is certainly possible, but it isn’t the easiest thing to do for people who don’t understand routing well. Here’s an example of how the route shown above could be defined:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "CustomerOrdersApiGet",
            routeTemplate: "api/customers/{custID}/orders",
            defaults: new { custID = 0, controller = "Customers", action = "Orders" }
        );

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpFormatter());
    }
}

With attribute based routing, defining these types of nested routes is greatly simplified. To get started you first need to make a call to the new MapHttpAttributeRoutes() method in the standard WebApiConfig class (or a custom class that you may have created that defines your routes) as shown next:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {

        // Allow for attribute based routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Once attribute based routes are configured, you can apply the Route attribute to one or more controller actions. Here’s an example:

[HttpGet]
[Route("customers/{custId:int}/orders")]
public List<Order> Orders(int custId)
{
    var orders = _Repository.GetOrders(custId);
    if (orders == null)
    {
        throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
    }
    return orders;
}

This example maps the custId route parameter to the custId parameter in the Orders() method and also ensures that the route parameter is typed as an integer. The Orders() method can be called using the following route:

/customers/2/orders

While this is extremely easy to use and gets the job done, it doesn’t include the default “api” string on the front of the route that you might be used to seeing. You could add “api” in front of the route and make it “api/customers/{custId:int}/orders” but then you’d have to repeat that across other attribute-based routes as well. To simply this type of task you can add the RoutePrefix attribute above the controller class as shown next so that “api” (or whatever the custom starting point of your route is) is applied to all attribute routes:

[RoutePrefix("api")]
public class CustomersController : ApiController
{
    [HttpGet]
    [Route("customers/{custId:int}/orders")]
    public List<Order> Orders(int custId)
    {
        var orders = _Repository.GetOrders(custId);
        if (orders == null)
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
        return orders;
    }
}

There’s much more that you can do with attribute-based routing in ASP.NET. Check out the following post by Mike Wasson for more details.

Returning Responses with IHttpActionResult 

The first version of Web API provided a way to return custom HttpResponseMessage objects which were pretty easy to use overall. However, Web API 2 now wraps some of the functionality available in version 1 to simplify the process even more.
A new interface named IHttpActionResult (similar to ActionResult in ASP.NET MVC) has been introduced which can be used as the return type for Web API controller actions. To return a custom response you can use new helper methods exposed through ApiController such as:
  • Ok
  • NotFound
  • Exception
  • Unauthorized
  • BadRequest
  • Conflict
  • Redirect
  • InvalidModelState
Here’s an example of how IHttpActionResult and the helper methods can be used to cleanup code. This is the typical way to return a custom HTTP response in version 1:

public HttpResponseMessage Delete(int id)
{
    var status = _Repository.DeleteCustomer(id);
    if (status)
    {
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
    else
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
}

With version 2 we can replace HttpResponseMessage with IHttpActionResult and simplify the code quite a bit:

public IHttpActionResult Delete(int id)
{
    var status = _Repository.DeleteCustomer(id);
    if (status)
    {
        //return new HttpResponseMessage(HttpStatusCode.OK);
        return Ok();
    }
    else
    {
        //throw new HttpResponseException(HttpStatusCode.NotFound);
        return NotFound();
    }
}

You can also cleanup post (insert) operations as well using the helper methods. Here’s a version 1 post action:

public HttpResponseMessage Post([FromBody]Customer cust)
{
    var newCust = _Repository.InsertCustomer(cust);
    if (newCust != null)
    {
        var msg = new HttpResponseMessage(HttpStatusCode.Created);
        msg.Headers.Location = new Uri(Request.RequestUri + newCust.ID.ToString());
        return msg;
    }
    else
    {
        throw new HttpResponseException(HttpStatusCode.Conflict);
    }
}

This is what the code looks like in version 2:

public IHttpActionResult Post([FromBody]Customer cust)
{
    var newCust = _Repository.InsertCustomer(cust);
    if (newCust != null)
    {
        return Created<Customer>(Request.RequestUri + newCust.ID.ToString(), newCust);
    }
    else
    {
        return Conflict();
    }
}

More details on IHttpActionResult and the different helper methods provided by the ApiController base class can be found here
This article was written by @DanWahlin and originally appeared on asp.net blog