Chapter 3. ASP.NET Web API 101

It’s easier to plot a course once you’ve seen the map.

Now that we’ve established the context around why Web APIs are important for modern networked applications, in this chapter we’ll take a first look at ASP.NET Web API. ASP.NET Web API and the new HTTP programming model offer different capabilities for both building and consuming Web APIs. First we’ll explore some of the core Web API goals and the capabilities that support them. We’ll then see how those capabilities are exposed to your code in ASP.NET Web API by looking at its programming model. What better way to examine that than by looking at the code provided by the Visual Studio Web API project template? Finally, we’ll go beyond the default template code and construct our first “Hello World” Web API.

Core Scenarios

Unlike many technologies, ASP.NET Web API has a very well-documented and accessible history (some is recorded on CodePlex). From the beginning, the development team made the decision to be as transparent as possible so that the product could be influenced by the community of experts who would ultimately use it to build real systems. I’ve distilled all that history down to the core goals that ASP.NET was created to address:

  • First-class HTTP programming
  • Symmetric client and server programming experience
  • Flexible support for different formats
  • No more “coding with angle brackets”
  • Unit testability
  • Multiple hosting options

Now, while these are key goals, they by no means represent an exhaustive list of all that the framework enables. ASP.NET Web API brings together the best of Windows Communication Foundation (WCF), with its infinitely extensible architecture, client support, and flexible hosting model, and ASP.NET MVC, with its support for convention over configuration, improved testability, and advanced features such as model binding and validation. As we’ll begin to explore in this chapter, the result is a framework that is approachable for getting started, and easily customizable as your needs evolve.

First-Class HTTP Programming

When you are building modern Web APIs—especially for simpler clients such as mobile devices—the success of that API is often related to its expressiveness. And the expressiveness of a Web API depends on how well it uses HTTP as an application protocol. Using HTTP as an application protocol goes far beyond simply handling HTTP requests and producing HTTP responses. It means that the behavior of both the application and the underlying framework is controlled by HTTP control flow and data elements rather than by some additional data that is merely (and incidentally) transmitted via HTTP. For example, consider the following SOAP request used to communicate with a WCF service:

POST http://localhost/GreetingService.svc HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "HelloWorld"
Content-Length: 154

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
        <HelloWorld xmlns="http://localhost/wcf/greeting"/>
  </s:Body>
</s:Envelope>

In this example, the client issues a request to the server to obtain a friendly greeting message. As you can see, the request is being sent via HTTP. However, this is really where the association with HTTP stops. Rather than using HTTP methods (sometimes called verbs) to communicate the nature of the action requested of the service, the approach shown here sends all requests using the same HTTP method—POST—and wraps the application-specific details in both the HTTP request body and the custom SOAPAction header. As you might expect, we see the same pattern repeated on the response produced by the service:

HTTP/1.1 200 OK
Content-Length: 984
Content-Type: text/xml; charset=utf-8
Date: Tue, 26 Apr 2011 01:22:53 GMT

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <HelloWorldResponse xmlns="http://localhost/wcf/greeting">
      ...
    </HelloWorldResponse>
  </s:Body>
</s:Envelope>

As in the case of the request message, the protocol elements used to control the application—that is, the way that the client and server applications understand one another—have been pulled out of the HTTP elements and placed inside the XML bodies of the request and response, respectively.

In this approach, HTTP is not being used to express the application protocol, but rather as a simple transport for a different application protocol—SOAP, in this case. And while this can be a good thing for scenarios where a single service needs to communicate with similar clients across lots of different protocols, it can be problematic when the need is to communicate with lots of very different clients across a single protocol. These kinds of problems are illustrated perfectly in the case of Web APIs, where the diversity of not only clients, but also of the communication infrastructure between clients and services (e.g., the Internet), is large and constantly changing. In this world, clients and services should be optimized not for protocol independence but rather for creating a first-class experience around a common application protocol. In the case of applications that communicate via the Web, that protocol is HTTP.

At its core, ASP.NET Web API is built around a small set of HTTP primitive objects. The two most notable of these are HttpRequestMessage and HttpResponseMessage. The purpose of these objects is to provide a strongly typed view of an actual HTTP message. For example, consider the following HTTP request message:

GET http://localhost:50650/api/greeting HTTP/1.1
Host: localhost:50650
accept: application/json
if-none-match: “1”

Assuming that this request was received by an ASP.NET Web API service, we can access and manipulate the various elements of this request using code similar to the following in a Web API controller class:

var request = this.Request;
var requestedUri = request.RequestUri;
var requestedHost = request.Headers.Host;
var acceptHeaders = request.Headers.Accept;
var conditionalValue = request.Headers.IfNoneMatch;

This strongly typed model provides the correct level of abstraction over HTTP, empowering the developer to work directly with an HTTP request or response while freeing her from having to deal with lower-level issues such as raw message parsing or generation.

Symmetric Client and Server Programming Experience

One of the most appealing aspects of ASP.NET Web APIs being built around this HTTP-focused object library is that the library can be used not only on the server but also in client applications that are built using the .NET Framework. This means that the HTTP request shown here can be created with the same HTTP programming model classes that are ultimately used to work with the request inside of a Web API, as shown later in this chapter.

As you’ll see in Chapter 10, there’s a lot more to the HTTP programming model than simply manipulating the various data elements of requests and responses. Features like message handlers and content negotiation are built directly into the HTTP programming model so that you can take advantage of them on both the client and the server to deliver sophisticated client-server interactions while simultaneously reusing as much of your code as possible.

Flexible Support for Different Formats

Content negotiation will be talked about in much more depth in Chapter 13, but at a high level, it is the process whereby a client and server work together to determine the right format to use when exchanging representations over HTTP. There are several different approaches and techniques for performing content negotiation, and by default, ASP.NET Web API supports the server-driven approach using the HTTP Accept header to let clients choose between XML and JSON. If no Accept header is specified, ASP.NET Web API will return JSON by default (though, like most aspects of the framework, this default behavior can be changed).

For example, consider the following request to an ASP.NET Web API service:

GET http://localhost:50650/api/greeting HTTP/1.1

Because this request contains no Accept header to provide the server with a desired format, the server will return JSON. We can change this behavior by adding an Accept header to our request and specifying the correct media type identifier for XML:[1]

GET http://localhost:50650/api/greeting HTTP/1.1
accept: application/xml

No More “Coding with Angle Brackets”

As the .NET Framework has matured, one of the ever-growing complaints from developers has been related to the amount of XML configuration required to enable seemingly basic or even default scenarios. Even worse, because configuration controlled things such as which types should be loaded at runtime, configuration changes could introduce errors into a system that would not be caught by the compiler, but only at runtime. One of the biggest examples of this complaint can be found in ASP.NET Web API’s predecessor, WCF. And while WCF itself has improved in the amount of configuration it requires, the ASP.NET Web API team went a totally different direction by introducing an entirely code-based configuration mode. ASP.NET Web API configuration will be discussed at length in Chapter 11.

Unit Testability

As techniques such as test-driven development (TDD) and behavior-driven development (BDD) have become more popular, there has been a proportional increase in frustration with many of the popular service and web frameworks over their use of static context objects, sealed types, and deep inheritance trees. These techniques make unit testing challenging in that objects become difficult to create and initialize in isolation from the underlying runtime; they also are very hard to substitute with a “fake” instance for better testing isolation.

For example, ASP.NET relies heavily on the HttpContext object, while WCF similarly relies on OperationContext (or WebOperationContext depending on the type of service). The fundamental problem with static context objects like these is that they are set up by and rely on their respective framework’s runtimes. As a result, testing a service that was developed using these context objects requires actually starting a service host and running the service. And while this technique is generally acceptable for integration-style tests, it is unsuitable for a development style such as TDD, which relies on being able to run smaller unit tests very quickly.

One of the goals in ASP.NET Web API is to provide much-improved support for these kinds of development styles, and there are two characteristics of the framework that accomplish this goal. First, ASP.NET Web API has the same programming model as the MVC framework. This enables it to take advantage of the testability work that was done a few years ago, including abstractions to avoid having to use static context objects and wrappers so that “fake” instances can be supplied to unit tests.

Second, remember that ASP.NET Web API is built around the HTTP programming model. The objects in this model are effectively simple data structures that can be created, configured, passed to an action method as a parameter, and analyzed when returned. This enables unit tests to be authored in a very clean, focused manner. As ASP.NET Web API has evolved, testing has remained a focus area for the team, as evidenced by the HttpRequestContext class in Web API 2. Testability will be discussed at greater length in Chapter 17.

Multiple Hosting Options

Despite some of its lesser qualities, one of the greatest attributes of WCF was its ability to “self-host”--that is, its ability to run in any process, for example a Windows service, a console application, or Internet Information Services (IIS). In fact, this kind of hosting flexibility made its limitations in unit testing almost bearable…almost.

When consolidating WCF Web API with ASP.NET to form ASP.NET Web API, the team wanted to keep this self-hosting ability, and so ASP.NET Web API services, like WCF services, can run in whatever process you choose. We’ll look at hosting in much greater depth in Chapter 11.

Getting Started with ASP.NET Web API

Now that we’ve reviewed some of the goals behind the development of ASP.NET Web API, let’s dive in and take a look at some of the various elements that you will be working with as you create your own Web APIs. One of the simplest ways to accomplish this is by creating a brand new ASP.NET Web API project and looking at the artifacts that the project template creates. To create a new ASP.NET Web API project, navigate to the Web node in the New Project dialog and select ASP.NET Web Application (Figure 3-1).

MVC 4 Web Application project in the Visual Studio 2012 New Project dialog
Figure 3-1. MVC 4 Web Application project in the Visual Studio 2012 New Project dialog

Once you choose to create an ASP.NET Web application, you will be presented with an additional dialog that gives you the ability to choose various project configurations. From this dialog, you will see the option to create a Web API project (Figure 3-2).

Web API project type in the MVC 4 New Project dialog
Figure 3-2. Web API project type in the MVC 4 New Project dialog

The key thing to note through this process is that Web API is simply a different project template within the family of ASP.NET projects. This means that Web API projects share all of the same core components as the other web project types, and differ only by the files that the template creates for you as a starting point. It also means that it’s both valid and desired to include Web APIs in any of the other templates shown in Figure 3-2.

In fact, at the end of the day, ASP.NET Web API is simply a set of classes built on top of the Web API framework components and hosted by a process, whether it’s the ASP.NET runtime as the default template sets up or your own custom host (as we will describe in more detail later in this chapter). This means that a Web API can go into any type of project, whether it’s an MVC project, a console application, or a class library that you reference from multiple host projects.

The ASP.NET Framework components are made available to your Web API projects via the NuGet package management application. The NuGet packages listed in Table 3-1 are installed by the default project template. To create a Web API in your own project, you simply need to ensure that you’ve installed the same packages based on the level of functionality that you require.

Table 3-1. NuGet packages for ASP.NET Web API
Package name Package ID[a] Description Package dependencies[b]

Microsoft .NET Framework 4 HTTP Client Libraries

Microsoft.Net.Http

Provides the core HTTP programming model, including HttpRequestMessage and HttpResponsemessage

(none)

Microsoft ASP.NET Web API

Microsoft.AspNet.WebApi

NuGet meta-package[c] providing a single reference for installing everything needed to create and host a Web API in ASP.NET

Microsoft.AspNet.WebApi.WebHost

Microsoft ASP.NET Web API Client Libraries

Microsoft.AspNet.WebApi.Client

Contains extensions to the core .NET Framework 4 HTTP client libraries to enable features such as XML and JSON formatting as well as the ability to perform content negotiation

Microsoft.Net.Http Newtonsoft.Json[d]

Microsoft ASP.NET Web API Core Libraries

Microsoft.AspNet.WebApi.Core

Contains the core Web API programming model and runtime components including the key ApiController class

Microsoft.AspNet.WebApi.Client

Microsoft ASP.NET Web API Web Host

Microsoft.AspNet.WebApi.WebHost

Contains all of the runtime components needed to host a Web API in the ASP.NET runtime

Microsoft.Web.Infrastructure Microsoft.AspNet.WebApi.Core

[a] You can use the package ID to learn more about the package by appending it to the URL ID}.

[b] A NuGet package dependency means that when you install a package, NuGet will first attempt to install all of the packages on which that the package depends.

[c] A NuGet meta-package is a package that contains no actual content of its own, but only dependencies to other NuGet packages.

[d] While used by ASP.NET Web API, Newtonsoft.Json is an external component available for free download.

In addition to the NuGet packages that are installed as a part of the default project templates, the NuGet packages shown in Table 3-2 are also available.

Table 3-2. Additional NuGet packages available for ASP.NET Web API
Package name Package ID Description Package dependencies

Microsoft ASP.NET Web API Self Host

Microsoft.AspNet.WebApi.SelfHost

Contains all of the runtime components needed to host a Web API in a custom process (e.g., console application)

Microsoft.AspNet.WebApi.Core

Microsoft ASP.NET Web API OWIN

Microsoft.AspNet.WebApi.Owin

Allows you to host ASP.NET Web API within an OWIN server and provides access to additional OWIN features

Microsoft.AspNet.WebApi.Core, Microsoft.Owin, Owin

Looking at the set of NuGet packages as a graph may give you a better understanding of which package or packages to install in your project based on what you are trying to accomplish. For example, consider Figure 3-3.

NuGet package hierarchy for Web API
Figure 3-3. NuGet package hierarchy for Web API

As you can see from the dependency graph, installing any one of these NuGet packages will automatically install all of the NuGet packages that are connected, directly or indirectly, to it. For example, installing Microsoft.AspNet.WebApi will install Microsoft.AspNet.WebApi.WebHost, Microsoft.AspNet.WebApi.Core, Microsoft.Web.Infrastructure, Microsoft.AspNet.WebApi.Client, Newtonsoft.Json, and Microsoft.Net.Http.

Exploring a New Web API Project

Now that we’ve created a new web-hosted ASP.NET Web API project, we’ll explore some of the key elements created by the project template, which we will customize in order to create our own Web APIs. We will focus on two key files: WebApiConfig.cs and ValuesController.cs (Figure 3-4).

WebApiConfig.cs and ValuesController.cs within the Visual Studio 2013 Solution Explorer
Figure 3-4. WebApiConfig.cs and ValuesController.cs within the Visual Studio 2013 Solution Explorer

WebApiConfig

This C# or Visual Basic.NET file is located in the App_Start top-level folder and declares the class WebApiConfig. This class contains a single method called Register and is called by code in the Application_Start method inside of global.asax. As its name indicates, the purpose of the class is to register various aspects of Web API’s configuration. By default, the primary configuration code generated by the project template registers a default Web API route. This route is used to map inbound HTTP requests onto controller classes as well as parse out data elements that may be sent as a part of the URL and make those available to other classes in the processing pipeline. The default WebApiConfig class is shown in Example 3-1.

Example 3-1. Default WebApiConfig class
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();

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

If you are familiar with MVC development, then you may have observed that ASP.NET Web API provides a different set of extension methods to register its routes than the default MVC routes. For example, the very same new project containing the WebApiConfig class also contains the following:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index",
                id = UrlParameter.Optional }
        );
    }
}

Having two route configuration methods can be confusing on first inspection, so it’s worth explaining the high-level differences between them. The point to keep in mind here is that these “Map” methods are simply extension methods that create an instance of a route and add it to the route collection associated with the host. The difference between them, and the subsequent reason behind the two different methods, is in the fact that ASP.NET MVC and ASP.NET Web API use completely different route classes and even route collection types. The details of these types will be discussed in greater detail in Chapter 11, but the reason for breaking away from the routing types used by ASP.NET MVC was to enable ASP.NET Web API to split from much of the legacy that resided alongside the Route and RouteCollection classes in the System.Web assembly, thereby providing a great deal more flexibility in terms of hosting options. An immediate benefit of this design decision is ASP.NET Web API’s self-host capability.

Configuring ASP.NET Web API routing requires declaring and adding HttpRoute instances to the route collection. Even though HttpRoute instances are created with a different extension method than that used for ASP.NET MVC, the semantics are nearly identical, including elements such as route name, route template, parameter defaults, and even route constraints. As you can see in Example 3-1, the project template’s route configuration code sets up a default API route that includes a URI prefix of “api” followed by the controller name and an optional ID parameter. Without any modification, this route declaration is typically sufficient for getting started creating APIs that allow for fetching, updating, and deleting data. This is possible because of the way in which ASP.NET Web API’s controller class maps HTTP methods onto controller action methods. We’ll cover HTTP method mapping in more detail later in this chapter, as well as in much greater detail in Chapter 12.

ValuesController

The ApiController class, which is the parent class of ValuesController, is at the heart of ASP.NET Web API. While we can create a valid ASP.NET Web API controller by simply implementing the various members of the IHttpController interface, in practice we’ll create most ASP.NET Web API controllers by deriving from the ApiController class. This class plays the role of coordinating with various other classes in the ASP.NET Web API object model to perform a few key tasks in processing an HTTP request:

  • Select and run an action method on the controller class.
  • Convert elements of an HTTP request message into parameters on a controller action method and convert the return value of an action method into a valid HTTP response body.
  • Run various types of filters that have been configured for the action method, the controller, or globally.
  • Expose appropriate context state to the action methods of the controller class.

By deriving from ApiController and taking advantage of the key processing tasks that it performs, the ValuesController class that is included as a part of the Web API template shows the higher-level abstraction that can be built on top of ApiController. Example 3-2 shows the ValuesController code.

Example 3-2. Default ValuesController class
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 ValuesController class, while simple, provides a helpful first look at the controller programming model.

First, notice the names of the controller’s action methods. By default, ASP.NET Web API follows the convention of selecting an action method, in part, by comparing the HTTP method to the action name. More precisely, ApiController looks for a controller action method whose name starts with the appropriate HTTP method. Therefore, in Example 3-2, an HTTP GET request to /api/values will be served by the parameterless Get() method. The framework offers different ways to tailor this default name-matching logic and provides extensibility points, enabling you to replace it entirely if desired. More details on controller and action selection can be found in Chapter 12.

In addition to selecting an action method based on the HTTP method, ASP.NET Web API can select the action based on additional elements of the request, such as query string parameters. More importantly, the framework supports binding these request elements to parameters of the action method. By default, the framework uses a combination of approaches to accomplish parameter binding, and the algorithm supports both simple and complex .NET types. For HTTP responses, the ASP.NET Web API programming model enables action methods to return .NET types and it converts those values into the appropriate HTTP response message body using content negotiation. You can find much more detail on parameter binding and content negotiation in Chapter 13.

At this point, we’ve discussed some of ASP.NET Web API’s design, and we’ve scratched the surface of the programming model in looking at the code provided as a part of the project template. We’ll now go a bit deeper and create our first “Hello World” Web API.

“Hello Web API!”

For our first ASP.NET Web API, we’re going to build a simple greeting service. And what greeting is more ubiquitous in programming literature than “Hello World!”? Therefore, we’ll start out with this simple read-only greeting API and then add several improvements throughout the remainder of this chapter to illustrate other aspects of ASP.NET Web API’s programming model.

Creating the Service

To create the service, simply create a new ASP.NET Web Application from Visual Studio’s New Project dialog. From the Web Application Refinement dialog, select Web API. This action will create a new ASP.NET Web API project from the default template.

A read-only greeting service

Starting from the default Web API project template, add a new controller. You can do this by either adding a new class or leveraging the controller item template provided by Visual Studio. To add the controller using the item template, right-click on the controllers folder and select the Add → Controller option from the context menu (Figure 3-5).

Visual Studio context menu for adding a new controller
Figure 3-5. Visual Studio context menu for adding a new controller

This will display another dialog from which you’ll provide additional configuration details about the controller being created. We are going to create a controller called GreetingController and will use the Empty API controller item template (Figure 3-6).

Web API controller scaffolding
Figure 3-6. Web API controller scaffolding

Completing the item template dialog will produce a new GreetingController class, which derives from the ApiController class. To have our new API return a simple greeting, we need to add a method capable of responding to an HTTP GET request for the controller. Remember that because of our default routing rule, GreetingController will be selected for an HTTP request, api/greeting. Therefore, let’s add a simple method to handle GET requests as follows:

public class GreetingController : ApiController
{
    public string GetGreeting() {
        return "Hello World!";
    }
}

We can now test our Web API to see that it is in fact returning our simple greeting. For this, we’ll use the HTTP debugging proxy tool called Fiddler. One particularly helpful feature of Fiddler when it comes to testing Web APIs is its ability to compose HTTP messages and execute them. We can use this feature to test our greeting API as shown in Figure 3-7.

Using Fiddler to compose a new HTTP request
Figure 3-7. Using Fiddler to compose a new HTTP request

When we execute the request, we can then explore both the request and response using Fiddler’s session inspector as shown in Figure 3-8.

Examining HTTP requests and responses using Fiddler
Figure 3-8. Examining HTTP requests and responses using Fiddler

As expected, this basic HTTP GET request to the greeting service returns the simple string “Hello World!”

Content negotiation

Returning to Figure 3-8, take a closer look at the HTTP Content-Type response header. By default, ASP.NET Web API will transform return values from action methods into the JSON format using the popular Json.NET library first referenced in Figure 3-3. However, as described earlier in this chapter, ASP.NET Web API supports server-driven content negotiation, and by default supports content negotiation between JSON and XML. To see this in action, go back to Fiddler’s request composer and add the following line to the request headers text box:

accept: application/xml

Then execute the request again and notice that the response now contains the header Content-Type: application/xml and the response body is now formatted as XML (Figure 3-9).

The content-negotiated request and response for our basic greeting
Figure 3-9. The content-negotiated request and response for our basic greeting

Add a greeting

While it’s interesting to be able to get a greeting in different formats, any nontrivial API needs the ability to manipulate the state, or the data, of the system. Therefore, we’ll extend the greeting service and give clients the ability to add new greetings. The idea is that a client can specify a greeting name and message, add it to the service, and then GET it again later via a URL that includes the greeting name. In addition, we need to handle cases where a client misspells or otherwise incorrectly specifies the greeting name in the URL by returning an HTTP 404 status code indicating that the resource could not be found.

To allow the client to create new greetings on the server, we need to create a model class to hold the greeting message’s name and message properties. We accomplish this by adding the following class to the project’s Models folder:

public class Greeting
{
    public string Name    {
        get;
        set;
    }

    public string Message    {
        get;
        set;
    }
}

We then create an action method on the GreetingController, which handles the HTTP POST request and is capable of accepting a Greeting instance as a parameter.

The action adds the greeting to a static list of greetings and returns an HTTP 201 status code along with a Location header pointing to the URL of the newly created greeting resource. The additional location header makes it possible for clients to simply follow the link value rather than requiring them to construct the URL for the new greeting resource, thereby making them more resilient since server URL structures can change over time:

public static List<Greeting> _greetings = new List<Greeting>();

public HttpResponseMessage PostGreeting(Greeting greeting) {
    _greetings.Add(greeting);

    var greetingLocation = new Uri(this.Request.RequestUri,
        "greeting/" + greeting.Name);
    var response = this.Request.CreateResponse(HttpStatusCode.Created);
    response.Headers.Location = greetingLocation;

    return response;
}

After adding the new greeting to the static collection, we create a URI instance representing the location where the new greeting can be found in subsequent requests. We then create a new HttpResponseMessage using the CreateResponse factory method of the HttpRequestMessage instance provided by the ApiController base class. The ability to work with HTTP object model instances from within action methods is a key feature of ASP.NET Web API; it provides fine-grained control over the HTTP message elements, such as the location header, in a way that does not rely on static context objects like HttpContext or WebOperationContext. This proves particularly beneficial when it comes to writing unit tests for Web API controllers, as we’ll discuss next.

Finally, we need to add an overload for the GetGreeting method that is capable of fetching and returning a client-supplied, custom greeting:

public string GetGreeting(string id) {
    var greeting = _greetings.FirstOrDefault(g => g.Name == id);
    return greeting.Message;
}

This method simply looks up the first greeting where the Name property matches the supplied id parameter and then returns the Message property. It is worth noting that there is currently not any sort of input validation on the id parameter. This will be discussed in the next section.

By default, an HTTP POST body will be handled by a MediaTypeFormatter object that is chosen based on the Content-Type request header. Accordingly, the following HTTP request will be handled by the default JSON formatter, which will use Json.NET to deserialize the JSON string into an instance of the Greeting class:

POST http://localhost:50650/api/greeting HTTP/1.1
Host: localhost:50650
Content-Type: application/json
Content-Length: 43

{"Name": "TestGreeting","Message":"Hello!"}

This resulting instance can then be passed to the PostGreeting method, where it is added to the collection of greetings. After PostGreeting has processed the request, the client will see the following HTTP response:

HTTP/1.1 201 Created
Location: http://localhost:50650/api/greeting/TestGreeting

From the location header, the client can then issue a request for the new greeting:

GET http://localhost:50650/api/greeting/TestGreeting HTTP/1.1
Host: localhost:50650

And, as in the case of our initial read-only greeting service, the client can expect the following response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 8

"Hello!"

Handling errors

The previous HTTP exchange works wonderfully so long as the server never has any errors and all clients follow the same rules and conventions. However, what happens in the event of a server error or an invalid request? This is another area where the ability to create and work with instances of the HTTP object model proves quite helpful. In Example 3-3, we want the action method to return a greeting’s string given its name. However, if the requested greeting name is not found, we want to return a response with the HTTP 404 status code. For this task, ASP.NET Web API provides the HttpResponseException.

Example 3-3. Returning a 404 status code when a greeting cannot be found
public string GetGreeting(string id) {
    var greeting = _greetings.FirstOrDefault(g => g.Name == id);
    if (greeting == null)
        throw new HttpResponseException(HttpStatusCode.NotFound);
    return greeting.Message;
}

While it would have been reasonable to simply return a new HttpResponseMessage that included the 404 status code, this would have required always returning an HttpResponseMessage from the GetGreeting action method—unneccessarily overcomplicating the nonexception code path. Additionally, the response message would have needed to flow back through the entire Web API pipeline, which would likely be unnecessary in the case of an exception. For these reasons, we will throw an HttpResponseException rather than return an HttpResponseMessage from the action method. In the event that an exception contains a response body that supports content negotiation, you can use the Request.CreateErrorResponse method from the base controller class and pass the resulting HttpResponseMessage to the HttpResponseException constructor.

Testing the API

One additional benefit of working directly with HTTP object model instances rather than static context objects is that it enables you to write meaningful unit tests against your Web API controllers. Testing will be covered in greater depth in Chapter 17, but as an introductory example, let’s write a quick unit test for the GreetingController’s PostGreeting action method:

[Fact]
public void TestNewGreetingAdd()
{
    //arrange
    var greetingName = "newgreeting";
    var greetingMessage = "Hello Test!";
    var fakeRequest = new HttpRequestMessage(HttpMethod.Post,
        "http://localhost:9000/api/greeting");
    var greeting = new Greeting { Name =
        greetingName, Message = greetingMessage };

    var service = new GreetingController();
    service.Request = fakeRequest;

    //act
    var response = service.PostGreeting(greeting);

    //assert
    Assert.NotNull(response);
    Assert.Equal(HttpStatusCode.Created, response.StatusCode);
    Assert.Equal(new Uri("http://localhost:9000/api/greeting/newgreeting"),
        response.Headers.Location);
}

This test follows the standard arrange, act, assert pattern of writing unit tests. We create some control state, including a new HttpRequestMessage instance, to represent the entire HTTP request. We then call the method under test using the context and finally process a few assertions about the response. In this case, the response is an instance of HttpResponseMessage, and as a result, we are able to process assertions on data elements of the response itself.

The Client

As mentioned at the beginning of this chapter, one of the other key benefits to building ASP.NET Web API around a core HTTP programming model is the fact that the same programming model can be used to build great HTTP applications for both the server and the client. For example, we can use the following code to construct a request that will be handled by our first GetGreeting action method:

class Program
{
    static void Main(string[] args)
    {
        var greetingServiceAddress =
            new Uri("http://localhost:50650/api/greeting");

        var client = new HttpClient();
        var result = client.GetAsync(greetingServiceAddress).Result;
        var greeting = result.Content.ReadAsStringAsync().Result;

        Console.WriteLine(greeting);
    }
}

Just like on the server, the client code here creates and processes instances of HttpRequestMessage and HttpResponseMessage. Additionally, ASP.NET Web API extension components, such as media type formatters and message handlers, work for clients as well as servers.

The Host

Developing an ASP.NET Web API for hosting in a traditional ASP.NET application feels very much the same as developing any other ASP.NET MVC application. One of the great characteristics of ASP.NET Web API, however, is that it can be hosted in any process that you designate with hardly any additional work. Example 3-4 shows the code required for hosting our GreetingController in a custom host process (a console application, in this case).

Example 3-4. A simple Web API console host
class Program
{
    static void Main(string[] args)
    {
        var config = new HttpSelfHostConfiguration(
        new Uri("http://localhost:50651"));

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

        var host = new HttpSelfHostServer(config);

        host.OpenAsync().Wait();

        Console.WriteLine("Press any key to exit");
        Console.ReadKey();

        host.CloseAsync().Wait();
    }
}

In order to host our Web API in a custom process, we did not modify the controller and we didn’t have to add any magical XML in the app.config file. Rather, we simply created an instance of HttpSelfHostConfiguration, configured it with address and routing information, and then opened the host. Once the host is open and listening for requests, we block the main console thread in order to avoid closing the server. When the user chooses to close the host (by pressing any key), we close the Web API host and exit the console application. Hosting is discussed in greater detail in Chapter 11.

Conclusion

In this chapter, we described some of the key design goals behind ASP.NET Web API. We then used the Web API project template to see how the different components that compose the framework are organized and distributed via NuGet, and also to begin to explore the framework’s programming model by looking at the default template code. Finally, we wrote our own “Hello World!” Web API and took advantage of ASP.NET Web API’s self-hosting abilities.

The chapters that follow will drill into each of the topics introduced here and provide a great deal more depth, starting with Chapter 4, which explores the underlying mechanics that make the ASP.NET Web API work.

Get Designing Evolvable Web APIs with ASP.NET now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.