How to work with message handlers in Web API

Take advantage of message handlers to execute cross cutting concerns earlier in the Web API life cycle

Message handlers in Web API

Message handlers in Web API

Message handlers in Web API provide you the opportunity to process, edit, or decline an incoming request before it reaches the HttpControllerDispatcher. Message handlers are executed much earlier in the request processing pipeline, hence they are a great place to implement cross cutting concerns in Web API.

Implementing a custom message handler

All message handlers derive from the class HttpMessageHandler. To build your own message handler, you should extend the DelegatingHandler class. Note that the DelegatingHandler class in turn derives from the HttpMessageHandler class.

Consider the following Web API controller.

public class DefaultController : ApiController

    {

        public HttpResponseMessage Get()

        {

            return Request.CreateResponse(HttpStatusCode.OK, "Inside the Default Web API Controller...");           

        }

    }

To create a message handler, you need to extend the DelegatingHandler class and override the SendAsync method.

public class IDGHandler : DelegatingHandler

    {

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

           return base.SendAsync(request, cancellationToken);

        }

    }

The Web API request processing pipeline includes a few built-in message handlers. These include the following:

  • HttpServer -- this is used to retrieve the request from the host
  • HttpRoutingDispatcher -- this is used to dispatch the request based on the route configured
  • HttpControllerDispatcher -- this is used to send the request to the respective controller

You can add message handlers to the pipeline to perform one or more of the following operations.

  • Perform authentication and authorization
  • Logging the incoming requests and the outgoing responses
  • Add response headers to the response objects
  • Read or modify the request headers

The following code snippet shows how you can implement a simple message handler in Web API.

public class IDGHandler : DelegatingHandler

{

protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

            var response = new HttpResponseMessage(HttpStatusCode.OK)

            {

                Content = new StringContent("Inside the IDG message handler...")

            };

            var task = new TaskCompletionSource<HttpResponseMessage>();

            task.SetResult(response);

            return await task.Task;

        }

}

The IDG message handler doesn't process the request message -- it creates response message and then returns it. You can also call the base version of the SendAsync method if you wouldn't like to do anything with the incoming request as shown in the code listing below.

public class IDGHandler : DelegatingHandler

{

protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

            return await base.SendAsync(request, cancellationToken);

        }

}

You can also write code to log the Http requests and the responses that go out in the SendAsync method.

To execute the Web API you can use a test method like the one given below.

 [TestMethod]

        public void WebAPIControllerTest()

        {

            HttpClient client = new HttpClient();

            var result = client.GetAsync(new Uri("http://localhost/IDG/api/default/")).Result;

            string responseMessage = result.Content.ReadAsStringAsync().Result;

            Assert.IsTrue(result.IsSuccessStatusCode);

        }

When you execute the test method, the message "Inside the Default Web API Controller..." is returned as a response message and the test passes. Oh! We did create a message handler, but we are yet to register it to the message handling pipeline.

You would now need to let the Web API infrastructure know where your custom handler exists. To do this, you should register your custom handler in the pipeline. You can register the IDG custom message handler we just created in the Register method of the WebApiConfig class as shown below.

public static void Register(HttpConfiguration config)

{

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

}

When you execute the test method again, the text message "Inside the logging message handler..." is returned as a response message and the test passes.

Note that you can also register multiple message handlers to the message handling pipeline as shown in the code snippet below.

public static void Register(HttpConfiguration config)

{

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

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

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

}

The message handlers would be executed in the order in which they have been added to the pipeline and the response would be returned in the reverse order. In other words, at the time of the incoming request, the message handlers are executed in the order in which they are registered. During the outgoing response, the process is just reversed. So, the message handlers are executed in the reverse order of their registration to the pipeline.

You can also implement a message handler that inspects the incoming request and checks if the request contains a valid api key. If the api key is not present or is not valid, it returns an appropriate error message. The following code listing shows how you can do this -- I'm leaving it to you to write the code to validate the api key anyway.

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

        {

            string key = HttpUtility.ParseQueryString(request.RequestUri.Query).Get("key");

            string errorMessage = "You need to specify the api key to access the Web API.";

            try

            {

                if (!string.IsNullOrWhiteSpace(key))

                {

                    return base.SendAsync(request, cancellationToken);

                }

                else

                {

                    HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.Forbidden, errorMessage);

                    throw new HttpResponseException(response);

                }

            }

            catch

            {

                HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.InternalServerError, "An unexpected error occured...");

                throw new HttpResponseException(response);

            }

        }

This article is published as part of the IDG Contributor Network. Want to Join?

To comment on this article and other InfoWorld content, visit InfoWorld's LinkedIn page, Facebook page and Twitter stream.
From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.