New Features in ASP.NET Web API 2 - Part I

I’m a big fan of ASP.NET Web API. It provides a quick yet powerful way to build RESTful HTTP services that can easily be consumed by a variety of clients. While it’s simple to get started using, it has a wealth of features such as filters, formatters, and message handlers that can be used to extend it when needed. In this post I’m going to provide a quick walk-through of some of the key new features in version 2. I’ll focus on some two of my favorite features that are related to routing and HTTP responses and cover additional features in a future post.

 

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.

Conclusion

Although there are several additional features available in Web API 2 that I could cover (CORS support for example), this post focused on two of my favorites features. If you have .NET 4.5 available then I definitely recommend checking the new features out. Additional articles that cover features in ASP.NET Web API 2 can be found here.

Published Monday, November 11, 2013 11:05 AM by dwahlin
Filed under: , , ,

Comments

# New Features in ASP.NET Web API 2 - Part I &raquo; Wisconsin Web Works

Pingback from  New Features in ASP.NET Web API 2 - Part I &raquo; Wisconsin Web Works

# Dew Drop &#8211; November 12, 2013 (#1665) | Morning Dew

Tuesday, November 12, 2013 7:58 AM by Dew Drop – November 12, 2013 (#1665) | Morning Dew

Pingback from  Dew Drop &#8211; November 12, 2013 (#1665) | Morning Dew

# re: New Features in ASP.NET Web API 2 - Part I

Tuesday, November 12, 2013 8:43 AM by v008370

I have to be honest, I found routing to be more of a challenge than you in anything but the absolute simplest of use cases.  I'm looking forward to many improvements for more complex apps.

# New Features in ASP.NET Web API 2 - Part I | TechBlog

Tuesday, November 12, 2013 12:21 PM by New Features in ASP.NET Web API 2 - Part I | TechBlog

Pingback from  New Features in ASP.NET Web API 2 - Part I | TechBlog

# re: New Features in ASP.NET Web API 2 - Part I

Tuesday, November 12, 2013 12:57 PM by sunil

How can we unit test IHttpActionResult?

# re: New Features in ASP.NET Web API 2 - Part I

Tuesday, November 12, 2013 1:14 PM by dwahlin

sunil:

You'd test it really like any other interface. It looks like this (see the docs for more details):

   public interface IHttpActionResult

   {

       Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken);

   }

So, you'd call an action on your Web API controller that returns IHttpActionResult and then check the content of the HttpResponseMessage.

Dan

# New Features in ASP.NET Web API 2 - Part I | We...

Saturday, November 16, 2013 8:23 AM by New Features in ASP.NET Web API 2 - Part I | We...

Pingback from  New Features in ASP.NET Web API 2 - Part I | We...

# re: New Features in ASP.NET Web API 2 - Part I

Monday, December 9, 2013 9:24 AM by rbprajapati

I think routing id very difficult stuff but you describe it very easily :)

# re: New Features in ASP.NET Web API 2 - Part I

Thursday, December 12, 2013 9:53 AM by rbprajapati

Attribute Routing nice features

# re: New Features in ASP.NET Web API 2 - Part I

Monday, December 16, 2013 2:03 AM by logodivine

nice and useful features plus easy to manage.  i will be definitely using them from now onwards.

# Whats New in ASP.NET Web API 2 | Best Windows Web Hosting

Tuesday, December 17, 2013 5:00 AM by Whats New in ASP.NET Web API 2 | Best Windows Web Hosting

Pingback from  Whats New in ASP.NET Web API 2 | Best Windows Web Hosting

# WCF Service hosted in Azure WebSites

Saturday, February 15, 2014 12:41 PM by WCF Service hosted in Azure WebSites

Pingback from  WCF Service hosted in Azure WebSites