HTTP Message Handlers in WEB API

message handler is a class that receives an HTTP request and returns an HTTP response. Message handlers derive from the abstract HttpMessageHandler class.

Typically, a series of message handlers are chained together. The first handler receives an HTTP request, does some processing, and gives the request to the next handler. At some point, the response is created and goes back up the chain. This pattern is called a delegating handler.

Server-Side HTTP Message Handlers in WEB API

Talking about message handlers, they are fairly common and in fact the HTTP infrastructure in ASP.NET uses them a lot. This means that even if you don’t know it, but each HTTP request travels a message handler pipeline. The cool thing is that we can create our own custom message handlers (which I’ll call from now on delegating handlers) and extend the behavior of the already existing pipeline. Graphically, the result would look something like that:

In ASP.NET Web API Framework, there are two types of message handlers are available. They are as follows

  1. Server-Side HTTP Message Handlers
  2. Client-Side HTTP Message Handlers

In this article, I am going to discuss the Server-Side HTTP Message Handlers in Web API Framework with some real-time examples.

Server- side Message Handlers

On the Server side, the Web API pipeline uses some built-in Message Handlers. The term “built-in” implies that those handlers are already set up by the MVC Web API framework. Those are:

  • HttpServer: This handler collects requests from the host.
  • HttpRoutingDispatcher: It dispatches the request based on the route after fetching routing information from a routing table.
  • HttpControllerDispatcher: sends the request to a Web API controller.

You can add custom handlers to the pipeline. Message handlers are good for cross-cutting concerns that operate at the level of HTTP messages (rather than controller actions). For example, a message handler might:

  • Read or modify request headers.
  • Add a response header to responses.
  • Validate requests before they reach the controller.

So, those are the server-side pre-defined Message Handlers that have provide smooth message (read HTTP request and response) passing in Web API applications (In the server side). There are two questions that may arise in this context.

Custom delegating handlers

To write a custom message handler, your class needs to derive from System.Net.Http.DelegatingHandler and override the SendAsync method. The method takes an HttpRequestMessage as input and asynchronously returns an HttpResponseMessage. A typical implementation does the following:

  1. Process the request message.
  2. Call base.SendAsync to send the request to the inner handler.
  3. The inner handler returns a response message. (This step is asynchronous.)
  4. Process the response and return it to the caller.

When you want to just forward requests to other REST resources, the desired behavior might be slightly different, since in this scenario when we receive a request we want to get the response from the remote resource and already sent the response back to the requester. This means that we would need to short-circuit the entire pipeline.

protected override Task HttpResponseMessage SendAsync(HttpRequestMessage request,
    CancellationToken cancellationToken)
    {
        request.Headers.Remove("Host");
        request.RequestUri = new Uri(CreateForwardUri(request.RequestUri.AbsoluteUri));

        if (request.Method == HttpMethod.Get) request.Content = null;
        return _client.SendAsync(request);
    }

Note: The call to the base.SendAsync is asynchronous. If the handler does any work after this call, use the await keyword, as shown in the above example.

A delegating handler can also skip the inner handler and directly create the response.

using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace WebApi
{
    public class MessageHandler1:DelegatingHandler
    {
        protected async override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            Debug.WriteLine("Process request");
            // Call the inner handler.
            var response = await base.SendAsync(request, cancellationToken);
            Debug.WriteLine("Process response");
            return response;
        }
    }
}
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace WebApi
{
    public class MessageHandler2 : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // Create the response.
            var response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent("Hello!")
            };
            // Note: TaskCompletionSource creates a task that does not contain a delegate.
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
}

When a delegate handler creates the response without calling the base.SendAsync method, then the request skips the rest of the pipeline. This can be useful for a handler that validates the request creating an error response.

How to Add Custom HTTP Message Handlers to the Pipeline in ASP.NET Web API?

To add the Custom HTTP Message Handlers on the server-side, you need to add the Custom Http Message Handlers to the HttpConfiguration.MessageHandlers collection inside the Register method of the WebApiConfig class as shown in the below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace WebApi
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            config.MessageHandlers.Add(new MessageHandler1());
            config.MessageHandlers.Add(new MessageHandler2());
            // Web API routes
            config.MapHttpAttributeRoutes();

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

The Message Handlers are going to call in the same order as they appear in the MessageHandlers collection. The reason is they are nested and the response message travels in the other direction. That is, the last handler is the first one to get the response message.

Notice that you don’t need to set the inner handlers. That is the job of the Web API framework which will automatically connect the inner message 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.

0 0 vote
Article Rating

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x