joydip_kanjilal
Contributor

How to work with message handlers in Web API

opinion
Oct 12, 20155 mins
Software Development

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

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);

            }

        }

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author