WebAPI Tip #7: Beautiful Message Handlers

Thursday, April 4, 2013

The WebAPI framework is full of abstractions. There are controllers, filter providers, model validators, and many other components that form the plumbing of the framework. However, there are only three simple abstractions you need to know on a daily basis to build reasonable software with the framework.

- HttpRequestMessage

- HttpResponseMessage

- HttpMessageHandler

The names are self explanatory. I was tempted to say HttpRequestMessage represents an incoming HTTP message, but saying so would mislead someone into thinking the WebAPI is all about server-side programming. I think of HttpRequestMessage as an incoming message on the server, but the HttpClient uses all the same abstractions, and when writing a client the request message is an outgoing message.  There is a symmetric beauty in how these 3 core abstractions work on both the server and the client.

WebAPI Abstractions

There are 4 abstractions in the above class diagram because while an HttpMessageHandler takes an HTTP request and returns an HTTP response, it doesn't know how to work in a pipeline. Multiple handlers can form a pipeline, or a chain of responsibility, which is useful for processing a network request. When you host the WebAPI in IIS with ASP.NET, you'll have a pipeline in IIS feeding requests to a pipeline in ASP.NET, which in turn gives control to a WebAPI pipeline. It's pipelines all the way down (to the final handler and back). It's the WebAPI pipeline that ultimately calls an ApiController in a project. You might never need to write a custom message handler, but if you want to work in the pipeline to inspect requests, check cookies, enforce SSL connections, modify headers, or log responses, then those types of scenarios are great for message handlers.

The 4th abstraction, DelegatingHandler, is a message handler that already knows how to work in a pipeline, thanks to an intelligent base class and an InnerHandler property. The code in a DelegatingHandler derived class doesn't need to do anything special to work in a pipeline except call the base class implementation of the SendAsync method. Here is where things can get slightly confusing because of the name (SendAsync), and the traditional notion of a pipeline. First, let's take a look at a simple message handler built on top of DelegatingHandler (it doesn't do anything useful, except to demonstrate later points):

public class DateObsessedHandler : DelegatingHandler
{          
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        var requestDate = request.Headers.Date;
        // do something with the date ...
        
        var response =  await base.SendAsync(request, cancellationToken);

        var responseDate = response.Headers.Date;
        // do something with the date ...

        return response;
    }
}

First, there is nothing pipeline specific in the code except for deriving from the DelegatingHandler base class and calling base.SendAsync (in other words, you never have to worry about using InnerHandler).

Secondly, we tend to think of a message pipeline as unidirectional. A message arrives at one end of the pipeline and passes through various components until it reaches a terminal handler. The pipeline for WebAPI message handlers is is bidirectional. Request messages arrive and pass through the pipeline until some handler generates a response, at which point the response message flows back out of the pipeline.

handlers

The purpose of SendAsync then, isn't just about sending. The purpose is to take a request message, and send a response message.

All the code in the method before the call to base.SendAsync is code to process the request message. You can check for required cookies, enforce an SSL connection, or change properties of the request for handlers further down the pipeline (like changing the HTTP method when a particular HTTP header is present).

When the call to base.SendAsync happens, the message continues to flow through the pipeline until a handler generates a response, and the response comes back in the other direction. Symmetry.

All the code in the method after the call to base.SendAsync is code to process the response message. You can add additional headers to the response, change the status code, and perform other post processing activities.

Most message handlers will probably only inspect the request or the response, not both. My sample code wanted to show both to show the two part nature of SendAsync. Once I started thinking of SendAsync as being divided into two parts, and how the messages flow up and down the pipeline, working with the WebAPI became easier. In a future post we'll look at  a more realistic message handler that can inspect the request and short circuit the response.

Finally, it's easy to add a DelegatingHandler into the global message pipeline (this is for server code hosted in ASP.NET):

GlobalConfiguration.Configuration
                   .MessageHandlers
                   .Add(new DateObsessedHandler());

But remember, you can also use the same message handlers on the client with HttpClient and a client pipeline, which is a another reason the WebAPI is symmetrically beautiful.


Comments
gravatar Paul K Thursday, April 4, 2013
Clear, concise explanation with just the right level of examples. Thanks!
gravatar John Friday, April 5, 2013
I still like to think of the pipeline as unidirectional, just with the handler being in the middle rather than at the end.
Comments are closed.

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!