ASP.NET Web API - Professional ASP.NET MVC 5 (2014)

Professional ASP.NET MVC 5 (2014)

Chapter 11
ASP.NET Web API

—by Brad Wilson

What's In This Chapter?

· How to define ASP.NET Web API

· Using the new ASP.NET Project wizard

· The basics of writing an API controller

· Configuring web-hosted and self-hosted Web API

· How to add Web API and MVC routing

· How to bind parameters

· How to filter requests

· How to enable dependency injection

· Programmatically exploring APIs

· How to trace the application

· ProductsController: A real-life example

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

You can find the wrox.com code downloads for this chapter at http://www.wrox.com/go/proaspnetmvc5 on the Download Code tab. This download contains the completed project for this chapter.

During the late ‘90s, web development started shifting from static content to active content and applications, using server-based technologies such as CGI, Active Server Pages (ASP), Java, and PHP. This shift ignited a transformation that is ongoing today: moving applications—especially IT applications in businesses—from the desktop and into the browser. A major accelerator of that shift was XMLHTTP, originally shipped with Internet Explorer 5, which when combined with JavaScript, allowed web developers to communicate back from their browser applications to the server. Google showed the world what was possible with applications, such as Google Maps and Gmail, and now the world is awash in browser-based applications.

Early versions of ASP.NET MVC included the ability to write controllers that behaved more like APIs than web pages, through the use of things such as the JsonResult class. However, there was always a bit of a programming model mismatch, with API authors desiring to have complete control over HTTP, which ASP.NET's abstractions made difficult (and ASP.NET MVC did not resolve).

ASP.NET Web API, first shipped in 2011, resolves this discontinuity with a first class, HTTP-centric programming model. Web API 2, shipped with MVC 5, has several significant new and improved features for the API developer.

DEFINING ASP.NET WEB API

If one common denominator exists in digital communications today, it's the prevalence of HTTP. Not only have we had HTTP-based web browsers in our PCs for decades, many of us now carry significant computing power in our pockets every day in the form of smartphones. Applications frequently use HTTP and JSON as their communication channels to call home. A web application today probably isn't considered “done” until it offers some form of remotely accessible API, a phone app, or both.

When MVC developers ask me to give them the elevator pitch for Web API, I usually say: “ASP.NET MVC excels at accepting form data and generating HTML; ASP.NET Web API excels at accepting and generating structured data like JSON and XML.” MVC has flirted with providing structured data support (with JsonResult and the JSON value provider), but it still fell short in several ways that are important to API programmers, including:

· Dispatching to actions based on HTTP verbs rather than action names

· Accepting and generating content that might not necessarily be object oriented (not only XML, but also content such as images, PDF files, or VCARDs)

· Content type negotiation, which allows the developer to both accept and generate structured content independent of its wire representation

· Hosting outside of the ASP.NET runtime stack and IIS web server, something which WCF has been able to do for years

An important part of this story, though, is that the Web API team went to great lengths to allow you to leverage your existing ASP.NET MVC experience with controllers, actions, filters, model binders, dependency injection, and the like. Many of these same concepts appear in Web API in very similar forms, which makes applications that combine MVC and Web API seem well integrated.

Because ASP.NET Web API is an entirely separate framework, it probably warrants a book of its own. This chapter helps illustrate the similarities and differences between MVC and Web API and help you decide whether you want to start including Web API in your MVC projects.

GETTING STARTED WITH WEB API

ASP.NET MVC 5 ships as part of Visual Studio 2013 and as an add-on for Visual Studio 2012. The installer includes all the components of ASP.NET Web API 2.

The New ASP.NET Project wizard, shown in Figure 11.1, allows the user to add Web API features to any project type, including Web Forms and MVC applications. The special project type “Web API” not only includes the Web API binaries, but also a sample API controller (called ValuesController) and some MVC code that can automatically generate help pages for your Web APIs. The File New Item menu in Visual Studio includes templates for empty Web API controllers, as does the all new Add New Scaffolded Item context menu item.

image

Figure 11.1

WRITING AN API CONTROLLER

Web API ships with MVC, and both utilize controllers. However, Web API does not share the Model-View-Controller design of MVC. They both share the notion of mapping HTTP requests to controller actions, but rather than MVC's pattern of using an output template and view engine to render a result, Web API directly renders the resulting model object as the response. Many of the design differences between Web API and MVC controllers come from this core difference between the two frameworks. This section illustrates the basics of writing a Web API controller and actions.

Examining the Sample ValuesController

Listing 11.1 contains the ValuesController that you get when you create a new project using the Web API project template. The first difference you'll notice is that a new base class is used for all API controllers: ApiController.

Listing 11.1: ValuesControllers

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Net.Http;

using System.Web.Http;

namespace WebApiSample.Controllers

{

public class ValuesController : ApiController {

// GET api/values

public IEnumerable<string> Get() {

return new string[] { "value1", "value2" };

}

// GET api/values/5

public string Get(int id) {

return "value";

}

// POST api/values

public void Post([FromBody] string value) {

}

// PUT api/values/5

public void Put(int id, [FromBody] string value) {

}

// DELETE api/values/5

public void Delete(int id) {

}

}

}

The second thing you'll notice is that the methods in the controller return raw objects rather than views (or other action results). Instead of returning views composed of HTML, the objects that API controllers return are transformed into the best matched format that the request asked for. (We'll talk a little later on about how that process takes place, as well as the new action results that were added to Web API 2.)

The third difference owes to conventional dispatching differences between MVC and Web API. Whereas MVC controllers always dispatch to actions by name, Web API controllers by default dispatch to actions by HTTP verb. Although you can use verb override attributes such as [HttpGet] or [HttpPost], most of your verb-based actions will probably follow the pattern of starting the action name with the verb name. The action methods in the sample controller are named directly after the verb, but they could also have just started with the verb name (meaning Get and GetValues are both reachable with the GET verb).

It's also worth noting that ApiController is defined in the namespace System.Web.Http and not in System.Web.Mvc where Controller is defined. When we discuss self-hosting later, the reason for this will be clearer.

Async by Design: IHttpController

Listing 11.2 shows the interface of ApiController. If you compare this to the interface of MVC's Controller class, you'll see that some concepts are the same (controller context, ModelState, RouteData, Url helper class, User), some are similar but different (Request isHttpRequestMessage from System.Net.Http rather than HttpRequestBase from System.Web), and some are missing (most notably Response and anything related to MVC views). It's also worth noting that the public interface of this class has grown by a substantial margin compared to v1 due to the new action result methods.

Listing 11.2: ApiController public interface

namespace System.Web.Http {

public abstract class ApiController : IHttpController, IDisposable {

// Properties

public HttpConfiguration Configuration { get; set; }

public HttpControllerContext ControllerContext { get; set; }

public ModelStateDictionary ModelState { get; }

public HttpRequestMessage Request { get; set; }

public HttpRequestContext RequestContext { get; set; }

public UrlHelper Url { get; set; }

public IPrincipal User { get; }

// Request execution

public virtual Task<HttpResponseMessage>

ExecuteAsync(

HttpControllerContext controllerContext,

CancellationToken cancellationToken);

protected virtual void

Initialize(

HttpControllerContext controllerContext);

// Action results

protected virtual BadRequestResult

BadRequest();

protected virtual InvalidModelStateResult

BadRequest(

ModelStateDictionary modelState);

protected virtual BadRequestErrorMessageResult

BadRequest(

string message);

protected virtual ConflictResult

Conflict();

protected virtual NegotiatedContentResult<T>

Content<T>(

HttpStatusCode statusCode,

T value);

protected FormattedContentResult<T>

Content<T>(

HttpStatusCode statusCode,

T value,

MediaTypeFormatter formatter);

protected FormattedContentResult<T>

Content<T>(

HttpStatusCode statusCode,

T value,

MediaTypeFormatter formatter,

string mediaType);

protected virtual FormattedContentResult<T>

Content<T>(

HttpStatusCode statusCode,

T value,

MediaTypeFormatter formatter,

MediaTypeHeaderValue mediaType);

protected CreatedNegotiatedContentResult<T>

Created<T>(

string location,

T content);

protected virtual CreatedNegotiatedContentResult<T>

Created<T>(

Uri location,

T content);

protected CreatedAtRouteNegotiatedContentResult<T>

CreatedAtRoute<T>(

string routeName,

object routeValues,

T content);

protected virtual CreatedAtRouteNegotiatedContentResult<T>

CreatedAtRoute<T>(

string routeName,

IDictionary<string, object> routeValues,

T content);

protected virtual InternalServerErrorResult

InternalServerError();

protected virtual ExceptionResult

InternalServerError(

Exception exception);

protected JsonResult<T>

Json<T>(

T content);

protected JsonResult<T>

Json<T>(

T content,

JsonSerializerSettings serializerSettings);

protected virtual JsonResult<T>

Json<T>(

T content,

JsonSerializerSettings serializerSettings,

Encoding encoding);

protected virtual NotFoundResult

NotFound();

protected virtual OkResult

Ok();

protected virtual OkNegotiatedContentResult<T>

Ok<T>(

T content);

protected virtual RedirectResult

Redirect(

string location);

protected virtual RedirectResult

Redirect(

Uri location);

protected virtual RedirectToRouteResult

RedirectToRoute(

string routeName,

IDictionary<string, object> routeValues);

protected RedirectToRouteResult

RedirectToRoute(

string routeName,

object routeValues);

protected virtual ResponseMessageResult

ResponseMessage(

HttpResponseMessage response);

protected virtual StatusCodeResult

StatusCode(

HttpStatusCode status);

protected UnauthorizedResult

Unauthorized(

params AuthenticationHeaderValue[] challenges);

protected virtual UnauthorizedResult

Unauthorized(

IEnumerable<AuthenticationHeaderValue> challenges);

}

}

The ExecuteAsync method on ApiController comes from IHttpController, and as you would expect by its name, it means that all Web API controllers are asynchronous by design. You have no need for a separate class for sync versus async actions when you use Web API. It's also clear that the pipeline here is quite different from ASP.NET, because rather than having access to a Response object, API controllers are expected to return a response object of type HttpResponseMessage.

The HttpRequestMessage and HttpResponseMessage classes form the basis of the HTTP support in System.Net.Http. The design of these classes is quite different from ASP.NET's core runtime classes in that handlers in this stack are given a request message and expected to return a response message. Unlike in ASP.NET, the System.Net.Http classes have no static methods for getting access to information about the ongoing request. This also means that rather than writing directly to a response stream, the developer instead returns an object that describes the response (and can later render it when needed).

Incoming Action Parameters

To accept incoming values from the request, you can put parameters on your action, and just like MVC, the Web API framework will automatically provide values for those action parameters. Unlike MVC, there is a strong line drawn between values from the HTTP body and values taken from other places (like from the URI).

By default, Web API will assume that parameters that are simple types (that is, the intrinsic types, strings, dates, times, and anything with a type converter from strings) are taken from non-body values, and complex types (everything else) are taken from the body. An additional restriction exists as well: Only a single value can come from the body, and that value must represent the entirety of the body.

Incoming parameters that are not part of the body are handled by a model binding system that is similar to the one included in MVC. Incoming and outgoing bodies, on the other hand, are handled by a brand-new concept called formatters. Both model binding and formatters are covered in more detail later in this chapter.

Action Return Values, Errors, and Asynchrony

Web API controllers send values back to the client by way of the return value of the action. As you probably guessed by the signature of ExecuteAsync, actions in Web API can return HttpResponseMessage to represent the response to send back to the client. Returning a response object is a fairly low-level operation, so Web API controllers almost always return a raw object value (or sequence of values) or an action result (a class that implements IHttpActionResult) instead.

When an action returns a raw object, Web API will automatically convert it into a structured response in the desired format (such as JSON or XML) using a feature of Web API called Content Negotiation. As mentioned earlier, the extensible formatting mechanism that does this conversion will be covered later in the chapter.

This ability to return a raw object is very powerful, but you lose something with the shift away from ActionResult or IHttpActionResult; namely, the ability to return different values for success and failure. When the signature of your action is strongly tied to the type of the return value that you want to use for success, how can you easily support returning some different representation for errors? If you change the signature of the action to HttpResponseMessage, it complicates the controller action (and unit testing).

To solve this dilemma, Web API allows developers to throw HttpResponseException from their actions to indicate that they are returning an HttpResponseMessage rather than successful object data. In this way, actions that have errors can formulate a new response and throw the response exception, and the Web API framework will treat the response as though the action directly returned that response message. Successful responses, then, can continue to return their raw object data and gain the benefits of simpler unit testing.

Web API 2 introduced a better solution to this problem: the new action result classes. To return action results, Web API controller actions use a return value type of IHttpActionResult, much like you would with MVC controllers and ActionResult. The ApiControllerclass includes many sets of methods that directly return action results; their resulting behavior is described in the following list:

· BadRequest: Returns an HTTP 400 (“Bad Request”). Optionally includes either a message or an automatically formatted error class based on validation errors in a ModelStateDictionary.

· Conflict: Returns an HTTP 409 (“Conflict”).

· Content: Returns content (similar to the behavior of an action method that returns a raw object). Content format is automatically negotiated, or optionally the developer can specify the media type formatter and/or the content type of the response. The developer chooses which HTTP status code the response uses.

· Created: Returns an HTTP 201 (“Created”). The Location header is set to the provided URL location.

· CreatedAtRoute: Returns an HTTP 201 (“Created”). The Location header is set to the URL that is constructed based on the provided route name and route values.

· InternalServerError: Returns an HTTP 500 (“Internal Server Error”). Optionally includes content derived from the provided exception.

· Json: Returns an HTTP 200 (“OK”), with the provided content formatted as JSON. Optionally formats the content with the provided serializer settings and/or character encoding.

· NotFound: Returns an HTTP 404 (“Not Found”).

· Ok: Returns an HTTP 200 (“OK”). Optionally includes content whose format is automatically negotiated (to specify the exact format, use the Content method instead).

· Redirect: Returns an HTTP 302 (“Found”). The Location header is set to the provided URL location.

· RedirectToRoute: Returns an HTTP 302 (“Found”). The Location header is set to the URL that is constructed based on the provided route name and route values.

· ResponseMessage: Returns the provided HttpResponseMessage.

· StatusCode: Returns a response with the provided HTTP status code (and an empty response body).

· Unauthorized: Returns an HTTP 401 (“Unauthorized”). The authentication header is set to the provided authentication header values.

Note

When ASP.NET Web API 2 added support for action results, the action results needed to be added to the Web API pipeline without breaking any other existing features; in particular, filter attributes that run against action methods that use action results will see the rendered HttpResponseMessage in the pipeline, not the raw IHttpActionResult object. This means filters written for Web API 1 should continue to work as-is in Web API 2.

A final note about action return values: If your action is asynchronous in nature (that is, it consumes other asynchronous APIs), you can modify the signature of your action return value to be Task<T> and use the async and await features in .NET 4.5 to seamlessly convert your sequential code into asynchronous code. Web API understands when actions return Task<T> that it should simply wait for the task to be complete, and then unwrap the returning object of type T and treat it as if the action had returned that directly. This includes action results (for example, Task<IHttpActionResult>).

CONFIGURING WEB API

You might have been wondering about the Configuration property on the controller. In traditional ASP.NET applications, application configuration is done in Global.asax, and the application uses global state (including statics and thread local variables) to give access to the request and application configuration.

Web API was designed not to have any such static global values, and instead put its configuration into the HttpConfiguration class. This has two impacts on application design:

· You can run multiple Web API servers in the same application (because each server has its own non-global configuration)

· You can run both unit tests and end-to-end tests more easily in Web API because you contain that configuration in a single non-global object, as statics make parallelized testing much more challenging.

The configuration class includes access to the following items:

· Routes

· Filters to run for all requests

· Parameter binding rules

· The default formatters used for reading and writing body content

· The default services used by Web API

· A user-provided dependency resolver for DI on services and controllers

· HTTP message handlers

· A flag for whether to include error details such as stack traces

· A Properties bag that can hold user-defined values

How you create or get access to this configuration depends on how you are hosting your application: inside ASP.NET, WCF self-host, or the new OWIN self-host.

Configuration in Web-Hosted Web API

The default MVC project templates are all web-hosted projects because MVC only supports web-hosting. Inside the App_Startup folder are the startup configuration files for your MVC application. The Web API configuration code is in WebApiConfig.cs (or .vb), and looks something like this:

public static class WebApiConfig {

public static void Register(HttpConfiguration config) {

// Web API configuration and services

// Web API routes

config.Routes.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(

name: "DefaultApi",

routeTemplate: "api/{controller}/{id}",

defaults: new { id = RouteParameter.Optional }

);

}

}

Developers will make modifications to this file to reflect any configuration changes they want to make for their application. The default contains a single route as an example to get you started.

If you look inside Global.asax, you'll see that this configuration function is called by passing the WebApiConfig.Register method as a parameter to the GlobalConfiguration.Configure method. This is a change from Web API 1, where the WebApiConfig.Register method was directly called. The change facilitates usage of attribute routes (discussed later in this chapter) by ensuring that configuration is run in the correct sequence. Web-hosted Web API supports only a single server and single configuration file, and the developer is not responsible for creating these, only for configuring them as appropriate. The GlobalConfiguration class is found in the assembly System.Web.Http.WebHost.dll, as is the rest of the infrastructure needed to support web-hosted Web APIs.

Configuration in Self-Hosted Web API

The two other hosts that ship with Web API are a WCF-based self-host (contained in the assembly System.Web.Http.SelfHost.dll) and an OWIN-based self-host (contained in the assembly System.Web.Http.Owin.dll). Both of these self-host options are useful for when you want to host your APIs outside of a web project (which typically means inside of a console application or a Windows Service).

There are no built-in project templates for self-hosting because no limitation exists to the project type that you might want to use when self-hosting. The simplest way to get Web API running in your application is to use NuGet to install the appropriate self-host Web API package (either Microsoft.AspNet.WebApi.SelfHost or Microsoft.AspNet.WebApi.OwinSelfHost). Both packages include all the System.Net.Http and System.Web.Http dependencies automatically.

When self-hosting, you are responsible for creating the configuration and starting and stopping the Web API server as appropriate. Each self-host system uses a slightly different configuration system, as described in the following sections.

Configurating WCF Self-Host

The configuration class you need to instantiate is HttpSelfHostConfiguration, which extends the base HttpConfiguration class by requiring a base URL to listen to. After setting up the configuration, you create an instance of HttpSelfHostServer, and then tell it to start listening.

Here is a sample snippet of startup code for WCF self-host:

var config = new HttpSelfHostConfiguration("http://localhost:8080/");

config.Routes.MapHttpRoute(

name: "DefaultApi",

routeTemplate: "api/{controller}/{id}",

defaults: new { id = RouteParameter.Optional }

);

var server = new HttpSelfHostServer(config);

server.OpenAsync().Wait();

You should also shut down the server when you're done:

server.CloseAsync().Wait();

If you are self-hosting in a console app, you would probably run this code in your Main function. For self-hosting in other application types, just find the appropriate place to run application startup and shutdown code and run these things there. In both cases, the.Wait() call could (and should) be replaced with an async code (using async and await) if your application development framework allows you to write an asynchronous startup and shutdown code.

Configuration for OWIN Self-Host

OWIN (Open Web Interface for .NET) is a fairly new way to define web applications that helps isolate the application itself from the hosting and web server that will run the app. In this way, an application can be written such that it could be hosted inside of IIS, inside of a custom web server, or even inside of ASP.NET itself.

Note

The subject of OWIN is too large to cover inside a single chapter. The information provided in this section is just enough to get you started with using the OWIN self-host. For more information on OWIN, please visit the OWIN home page athttp://owin.org/ for more information.

The ASP.NET team has also started a project called Katana that provides a lot of infrastructure around OWIN, including hosting executables and interface libraries to allow OWIN applications to be run against HttpListener or IIS (with or without ASP.NET). For more information on Katana, please see http://www.asp.net/aspnet/overview/owin-and-katana/an-overview-of-project-katana.

Because OWIN is about abstracting the web server from the web application, you also need to choose a way to connect your app to your web server of choice. The NuGet package Microsoft.AspNet.WebApi.OwinSelfHost brings in parts of the Katana project to make it easy to self-host your Web APIs using HttpListener, which has no dependency on IIS.

Here is an example snippet of code that a console-based OWIN self-host application might use:

using (WebApp.Start<Startup>("http://localhost:8080/")) {

Console.WriteLine("Server is running. Press ENTER to quit.");

Console.ReadLine();

}

Note that this code contains no Web API references; instead, it “starts” another class called Startup. The definition of the Startup class that supports Web API might look something like this:

using System;

using System.Linq;

using System.Web.Http;

using Owin;

class Startup {

public void Configuration(IAppBuilder app) {

var config = new HttpConfiguration();

config.Routes.MapHttpRoute(

name: "DefaultApi",

routeTemplate: "api/{controller}/{id}",

defaults: new { id = RouteParameter.Optional }

);

app.UseWebApi(config);

}

}

The Startup class in an OWIN application conceptually replaces the WebApiConfig class that's used in web-hosted applications. The IAppBuilder type from OWIN allows you to configure the application that will run; here you use the UseWebApi extension method that the Web API OWIN Self Host package provides to configure OWIN.

Choosing between WCF and OWIN Self-Host

The fact that ASP.NET Web API offers two different self-host solutions can be confusing. When Web API 1 originally shipped with MVC 4, the OWIN framework had not reached 1.0, so the ASP.NET team decided to reuse the WCF hosting infrastructure for self-hosting.

Now that OWIN is complete, the ASP.NET team is investing heavily into OWIN hosting across many of its products, not just Web API. In addition, OWIN allows multiple application frameworks to easily co-exist with one another, and even lets those applications share common functionality (called “middleware”) such as authentication and caching support. Although OWIN only recently released as 1.0, it has been around in the community for several years, and many third-party application frameworks can run on top of OWIN (such as Nancy and FubuMVC). In addition, OWIN offers pluggable web server support through frameworks such as Katana (used by Web API's OWIN self-host library) and Nowin (a pure .NET-based web server).

For these reasons, I recommend that if you have new projects with self-hosted Web API, you should choose OWIN. Although nothing is wrong with the WCF self-host, it is clearly going to be a “legacy” solution, whereas most of ASP.NET moves forward on the OWIN platform.

ADDING ROUTES TO YOUR WEB API

As illustrated in the previous section, Web API's primary route registration is the MapHttpRoute extension method. As is the case for all Web API configuration tasks, the routes for your application are configured off the HttpConfiguration object.

If you peek into the configuration object, you'll discover that the Routes property points to an instance of the HttpRouteCollection class rather than ASP.NET's RouteCollection class. Web API offers several versions of MapHttpRoute that work against the ASP.NETRouteCollection class directly, but such routes are only usable when web-hosted, so we recommend (and the project templates encourage) that you use the versions of MapHttpRoute on HttpRouteCollection.

Note

The attribute-based routing feature that was introduced in MVC 5 is also available to your Web API 2 applications. To enable attribute routing for your Web API controllers, add the following line of code to your Web API startup code, before any of your hand-configured routes:

config.MapHttpAttributeRoutes();

The routing system in Web API uses the same routing logic that MVC uses to help determine which URIs should route to the application's API controllers, so the concepts you know from MVC apply to Web API, including the route matching patterns, defaults, and constraints. To keep Web API from having any hard dependencies on ASP.NET, the team took a copy of the routing code from ASP.NET and ported it to Web API. The way this code behaves changes slightly depending on your hosting environment.

When running in the self-hosted environment, Web API uses its own private copy of the routing code, ported from ASP.NET into Web API. Routes in Web API will look much the same as those in MVC, but with slightly different class names (HttpRoute versus Route, for example).

When your application is web-hosted, Web API uses ASP.NET's built-in routing engine because it's already hooked into the ASP.NET request pipeline. When registering routes in a web-hosted environment, the system will not only register your HttpRoute objects, but it will also automatically create wrapper Route objects and register them in the ASP.NET routing engine. The major difference between self-hosting and web-hosting is when routing is run; for web-hostin, routing is run fairly early (by ASP.NET), whereas in the self-host scenario, routing is fairly late (by Web API). If you are writing a message handler, it's important to note that you might not have access to routing information, because routing might not yet have been run.

The most significant difference between the default MVC route and the default Web API route is the lack of the {action} token in the latter. As discussed earlier, Web API actions are dispatched to by default based on the HTTP verb that the request used. However, you can override this mapping by using the {action} matching token in the route (or by adding an action value to the default values for the route). When the route contains an action value, Web API uses that action name to find the appropriate action method.

Even when using action name -based routing, the default verb mappings do still apply; that is, if the action name starts with one of the well-known verb names (Get, Post, Put, Delete, Head, Patch, and Options), then it's matched to that verb. For all the actions whose names don't match one of the well-known verbs, the default supported verb is POST. You should decorate your actions using the [Http...] family of attributes ([HttpDelete], [HttpGet], [HttpHead], [HttpOptions], [HttpPatch], [HttpPost] and [HttpPut]) or the[AcceptVerb] attribute to indicate what verb(s) should be allowed when the default conventions aren't correct.

BINDING PARAMETERS

The earlier discussion about “body values” and “non-body values” leads us to discuss Formatters and Model Binders because those two classes are responsible for handling bodies and non-body values, respectively.

When you write an action method signature and include parameters, complex types come from “the body,” which really means that formatters are responsible for generating them; simple types, on the other hand, come from “not the body,” which means that model binders are responsible for generating them. For body content being sent, you use formatters to decode the data.

To tell the whole story, though, you need to rise up a level into a concept that is new to Web API: Parameter Binding. Web API uses parameter binders to determine how to provide values for individual parameters. You can use attributes to influence that decision (such as [ModelBinder], an attribute we've seen before with MVC), but the default logic uses the simple type versus complex type logic when there are no overrides applied to influence the binding decision.

The Parameter Binding system looks to the action's parameters to find any attributes that derive from ParameterBindingAttribute. The following list shows a few such attributes that are built into Web API. In addition, you can register custom parameter binders that do not use model binding or formatters, either by registering them in the configuration or by writing your own ParameterBindingAttribute-based attributes.

· ModelBinderAttribute: This tells the parameter binding system to use model binding (meaning, create the value through the use of any registered model binders and value providers). This is what is implied by the default binding logic for any parameter of a simple type.

· FromUriAttribute: This is a specialization of ModelBindingAttribute that tells the system only to use value providers from factories, which implement IUriValueProviderFactory to limit the values bound to ensure that they come only from URI. Out of the box, the route data and query string value providers in Web API implement this interface.

· FromBodyAttribute: This tells the parameter binding system to use formatters (meaning, create the value by finding an implementation of MediaTypeFormatter, which can decode the body and create the given type from the decoded body data). This is what is implied by the default binding logic for any complex type.

The parameter binding system is quite different from the way MVC works. In MVC, all parameters are created through model binding. Model binding in Web API works mostly the same way as MVC (model binders and providers, and value providers and factories), although it's been re-factored quite a bit, based on the alternate model binding system from MVC Futures. You will find built-in model binders for arrays, collections, dictionaries, simple types, and yes, even complex types (though you would need to use[ModelBinder] to get them to run, obviously). Although the interfaces have changed slightly, if you know how to write a model binder or value provider in MVC, you'll be right at home doing the same thing for Web API.

Formatters are a new concept for Web API. Formatters are responsible for both consuming and producing body content. You can think of them in much the same way you might think of serializers in .NET: classes that are responsible for encoding and decoding custom complex types into and out of the stream of bytes, which is the body content. You can encode exactly one object into the body, and decode exactly one object back out of the body (although that object can contain nested objects, as you would expect of any complex type in .NET).

Built into Web API you will find three formatters, one which:

· Encodes and decodes JSON (using Json.NET)

· Encodes and decodes XML (using either DataContractSerializer or XmlSerializer)

· Decodes form URL encoded from data in the body from a browser form post.

Each of these formatters is quite powerful, and will make its best effort to transcode its supported format into the class of your choosing.

Note

Although much of Web API is designed to support writing API servers, the built-in JSON and XML formatters are useful for client applications as well. The HTTP classes in System.Net.Http are all about raw HTTP and do not include any kind of object-to-content mapping system like formatters.

The Web API team chose to put the formatters into a stand-alone DLL named System.Net.Http.Formatting. Because this DLL has no dependencies other than System.Net.Http, it's usable for both client and server HTTP code—a wonderful benefit if you are also writing a .NET-based client application that consumes the Web API service you are writing.

The DLL contains several helpful extension methods for HttpClient, HttpRequestMessage, and HttpResponseMessage that allow you to easily use the built-in formatters in both client and server applications. (Note that the form URL encoded formatter was put into this DLL, but because it only supports decoding form data posted from browsers and not encoding, it is most likely of limited value to client applications.)

FILTERING REQUESTS

The ability to filter requests with attributes has been in ASP.NET MVC since version 1.0, and the ability to add global filters was added in MVC 3. ASP.NET Web API includes both features, although as discussed previously, the filter is global at the configuration level, not at the application level (as no such application-wide global features exist in Web API).

One of the improvements in Web API over MVC is that filters are now part of the asynchronous pipeline, and are by definition always async. If a filter could benefit from being asynchronous—for example, logging exception failures to an asynchronous data source such as a database or the file system—then it can do so. However, the Web API team also realized that sometimes being forced to write asynchronous code is unnecessary overhead, so they also created synchronous attribute-based base class implementations of the three filter interfaces. When porting MVC filters, using these base classes is probably the simplest way to get started. If a filter needs to implement more than one stage of the filter pipeline (such as action filtering and exception filtering), there are no helper base classes and the interfaces need to be implemented explicitly.

Developers can apply filters at the action level (for a single action), at the controller level (for all actions in the controller), and at the configuration level (for all actions on all controllers in the configuration). Web API includes one filter in the box for developers to use, AuthorizeAttribute. Much like its MVC counterpart, this attribute is used to decorate actions that require authorization, and includes the AllowAnonymousAttribute, which can selectively “undo” the AuthorizeAttribute. The Web API team also released an out-of-band NuGet package to support several OData-related features, including QueryableAttribute, which can automatically support OData query syntax (such as the $top and $filter query string values).

· IAuthenticationFilter: Authentication filters identify the user making the request. In previous versions of ASP.NET MVC and Web API, authentication was not easily pluggable; you either depended on built-in behavior from your web server, or you co-opted another filter stage like authorization. Authentication filters run before authorization filters.

· IAuthorizationFilter / AuthorizationFilterAttribute: Authorization filters run before any parameter binding has happened. They are intended to filter out requests that do not have the proper authorization for the action in question. Authorization filters run before action filters.

· IActionFilter / ActionFilterAttribute: Action filters run after parameter binding has happened and wrap the call to the API action method, allowing interception before the action has been dispatched to and after it is done executing. They are intended to allow developers to either augment and/or replace the incoming values and/or outgoing results of the action.

· IExceptionFilter / ExceptionFilterAttribute: Exception filters are called when calling the action resulted in an exception being thrown. Exception filters can inspect the exception and take some action (for example, logging); they can also opt to handle the exception by providing a new response object.

You have no equivalent to the MVC HandleError attribute in Web API. When an MVC application encounters an error, its default behavior is to return the ASP.NET “yellow screen of death.” This is appropriate (if not entirely user friendly) when your application is generating HTML. The HandleError attribute allows MVC developers to replace that behavior with a custom view. Web API, on the other hand, should always attempt to return structured data, including when error conditions occur, so it has built-in support for serializing errors back to the end user. Developers who want to override this behavior can write their own error handler filter and register it at the configuration level.

ENABLING DEPENDENCY INJECTION

ASP.NET MVC 3 introduced limited support for dependency injection containers to provide both built-in MVC services and the ability to be the factory for non-service classes such as controllers and views. Web API has followed suit with similar functionality, with two critical differences.

First, MVC used several static classes as the container for the default services consumed by MVC. Web API's configuration object replaces the need for these static classes, so the developer can inspect and modify this default service listed by accessingHttpConfiguration.Services.

Second, Web API's dependency resolver has introduced the notion of “scopes.” A scope can be thought of as a way for a dependency injection container to keep track of the objects that it has allocated in some particular context so that they can be easily cleaned up all at once. Web API's dependency resolver uses two scopes:

· A per-configuration scope—For services global to the configuration, cleaned up when the configuration is disposed

· A request-local scope—For services created in the context of a given request, such as those consumed by a controller, and cleaned up when the request is completed

Chapter 13 contains more detailed information on using dependency injection in both MVC and Web API scenarios.

EXPLORING APIS PROGRAMMATICALLY

An MVC application's controllers and actions are usually a fairly ad-hoc affair, designed solely to suit the display of HTML in the application. Web APIs, on the other hand, tend to be more ordered and planned. Offering the ability to discover APIs at run time enables developers to provide key functionality along with their Web API applications, including things like automatically generated help pages and testing client UI.

Developers can acquire the IApiExplorer service from HttpConfiguration.Services and use it to programmatically explore the APIs exposed by the service. For example, an MVC controller could return the IApiExplorer instance from Web API to this snippet of Razor code to list all the available API endpoints. (Figure 11.2 shows the output of this code.)

@model System.Web.Http.Description.IApiExplorer

@foreach (var api in Model.ApiDescriptions) {

<h1>@api.HttpMethod @api.RelativePath</h1>

if (api.ParameterDescriptions.Any()) {

<h2>Parameters</h2>

<ul>

@foreach (var param in api.ParameterDescriptions) {

<li>@param.Name (@param.Source)</li>

}

</ul>

}

}

image

Figure 11.2

In addition to the automatically discoverable information, developers can implement the IDocumentationProvider interface to supplement the API descriptions with documentation text, which could be used to offer richer documentation and test client functionality. Because the documentation is pluggable, developers can choose to store the documentation in whatever form is convenient, including attributes, stand-alone files, database tables, resources, or whatever best suits the application build process.

For a more complete example of what's possible with these APIs, you can install the Microsoft.AspNet.WebApi.HelpPage Nuget package into a project with both MVC and Web API. This package is a good starting point for developers who want to ship automated documentation along with their web APIs.

TRACING THE APPLICATION

One of the most challenging things with remotely deployed code is debugging when something has gone wrong. Web API enables a very rich automatic tracing ecosystem that is turned off by default but can be enabled by the developer as needed. The built-in tracing functionality wraps many of the built-in components and can correlate data from individual requests as it moves throughout the layers of the system.

The central part of tracing is the ITraceWriter service. Web API does not ship with any implementations of this service because it is anticipated that developers will likely already have their own favorite tracing system (such as ETW, log4net, ELMAH, or many others). Instead, on startup Web API checks whether an implementation of ITraceWriter is available in the service list, and if so, automatically begins tracing all requests. The developer must choose how best to store and browse this trace information—typically, by using the configuration options provided by their chosen logging system.

Application and component developers can also add tracing support to their systems by retrieving the ITraceWriter service and, if it's not null, writing tracing information to it. The core ITraceWriter interface only contains a single Trace method, but several extension methods are designed to make tracing different levels of messages (debug, info, warning, error, and fatal messages) easy. You also have helpers to trace entry and exit to both synchronous and asynchronous methods.

WEB API EXAMPLE: PRODUCTSCONTROLLER

Here's an example Web API controller that exposes a simple data object through Entity Framework's Code First feature. To support this example, you will need three files:

· The model—Product.cs (Listing 11.3)

· The database context—DataContext.cs (Listing 11.4)

· The controller—ProductsController.cs (Listing 11.5)

Listing 11.3: Product.cs

public class Product

{

public int ID { get; set; }

public string Name { get; set; }

public decimal Price { get; set; }

public int UnitsInStock { get; set; }

}

Listing 11.4: DataContext.cs

public class DataContext : DbContext

{

public DbSet<Product> Products { get; set; }

}

Listing 11.5: ProductsController.cs

public class ProductsController : ApiController

{

private DataContext db = new DataContext();

// GET api/Products

public IEnumerable<Product> GetProducts()

{

return db.Products;

}

// GET api/Products/5

public IHttpActionResult GetProduct(int id)

{

Product product = db.Products.Find(id);

if (product == null)

{

return NotFound();

}

return Ok(product);

}

// PUT api/Products/5

public IHttpActionResult PutProduct(int id, Product product)

{

if (ModelState.IsValid && id == product.ID)

{

db.Entry(product).State = EntityState.Modified;

try

{

db.SaveChanges();

}

catch (DbUpdateConcurrencyException)

{

return NotFound();

}

return Ok(product);

}

else

{

Return BadRequest(ModelState);

}

}

// POST api/Products

public IHttpActionResult PostProduct(Product product)

{

if (ModelState.IsValid)

{

db.Products.Add(product);

db.SaveChanges();

var uri = new Uri(

Url.Link(

"DefaultApi",

new { id = product.ID }));

return Created(uri, product);

}

else

{

Return BadRequest(ModelState);

}

}

// DELETE api/Products/5

public IHttpActionResult DeleteProduct(int id)

{

Product product = db.Products.Find(id);

if (product == null)

{

return NotFound();

}

db.Products.Remove(product);

try

{

db.SaveChanges();

}

catch (DbUpdateConcurrencyException)

{

return NotFound();

}

return Ok(product);

}

protected override void Dispose(bool disposing)

{

db.Dispose();

base.Dispose(disposing);

}

}

SUMMARY

ASP.NET Web API is a powerful new way to add APIs to your new and existing web applications. MVC developers will find its controller-based programming model familiar, and WCF developers will find its support for both web-hosting and self-hosting to be an added bonus compared to MVC-based service systems. When coupled with async and await in .NET 4.5, the asynchronous design allows your Web APIs to scale efficiently while maintaining a comfortable sequential programming model.