Introduction
[Level T3] MessageHandler is a new concept in ASP.NET Web API and a very useful tool in separation of concerns in a Web API service. In this article we will review the concept and outline its usefulness with a few simple examples.
Background
IIS as the Windows platform's HTTP server implementation, has always had two concepts: a component that receives HTTP request and responsible for returning a response (endpoint of communication), and another component that sniffs the communication and can quietly read/write from/to the request/response.
In the old times of unmanaged C++ IIS application implementation, we had ISAPI extensions and ISAPI filters. So the first one was responsible for reading request and sending back a response while the other, would be sniffing incoming and outgoing messages and perhaps sneak in/out a few headers or content. Difference also explained here.
In ASP.NET we had the same two familar concepts: HTTP Handlers and HTTP Modules with corresponding IHttpHandler and IHttpModule interfaces, respecitvely. Again here, we had handlers responsible for handling the request and returning a response, while HttpModule would be sitting as a not-so-innocent-bystander and sniff in what goes and chip in when necessary.
To illustrate this further, all Web Forms pages or MVC controllers can be looked as HTTP Handlers (although the interface itself would be implemented much higher in the pipeline but would delegate the responsibility to page or controller action). On the other hand, useful tools such as Glimpse or ELMAH use HTTP module to intercept the errors or routes from the stream of incoming/outgoing request/response. ASP.NET MVC has a concept of ActionFilter which is similar but for the sake of brevity it is better not to delve into it.
ASP.NET Web API continues these two concepts yet combines them into HttpMessageHandler which is the base class for important classes in the stack such as HttpServer, HttpControllerDispatcher and HttpRoutingDispatcher. This class can be implemented as both an equivalent of HTTP module or handler. Yet DelegatingHandler, a subclass of HttpMessageHandler has been specialised into an equivalent of HTTP module. While understanding of HttpMessageHandler is important, it is much more likely that you will only implement DelegatingHandler. As such we cover it in more details.
DelegaingHandler in Web API
DelegatingHandlers are used in the Web API to represent Message Handlers before routing (see below). They are ordered modules in the HTTP pipeline stream each one receiving request, doing some work and pass to the next. On the outbound stream also response is taken, processed passed to the next. However, at any point, a handler can decide to take action and process the request returning the response without passing to the next one. If I need to show this, I would use my very crude drawing techniques:Request/Response pipeline and Message Handlers in ASP.NET Web API - in a crude way :) |
So now let's think about it. We have a stack of message handlers: first handler to receive the request is the last one to be passed the response, vice versa. This is similar to Russian Dolls, each inside the other.
Russian doll model
If you have not seen Russian dolls, well there it is for you:
The idea is that you you buy just the biggest doll and once you open it, you find a smaller one and if you open that one, you see a smaller... until you get to the last one.
So let's look at the DelegatingHandler and other classes in the hierarchy:
As can be seen, DelegatingHandler is a derived class from HttpMessageHandler and has a property of InnderHandler. This inner handler in fact is the smaller Russian Doll inside the handler. So as you can imagine all these handlers are chained together using this InnerHandler property.
There is in fact some code in ASP.NET Web API that turns IEnumerable<DelegatingHandler> into a chain of DelegatingHandlers.
Basically in a delegating handler, if you need to inspect the request then you would simply do it in the SendAsync. However, if you need to work on the response, you would do it in the continuation of the Task.
So let's see some code. Let's imagine I need to write to the trace all incoming request URLs and add a custom response header identifying that this URL has been written to the trace (a very fictitious case, we will see some real world scenarios in the next post), we can code it very easily:
Class hierarchy of Message Handlers in System.Net.Http.dll |
As can be seen, DelegatingHandler is a derived class from HttpMessageHandler and has a property of InnderHandler. This inner handler in fact is the smaller Russian Doll inside the handler. So as you can imagine all these handlers are chained together using this InnerHandler property.
There is in fact some code in ASP.NET Web API that turns IEnumerable<DelegatingHandler> into a chain of DelegatingHandlers.
How does a DelegatingHandler work?
A DelegatingHandler mainly has to implement SendAsync method. This method receives a HttpRequestMessage (and a cancellation token) and must return a Task<HttpResponseMessage>. So you might have expected to see two methods, one for handling request and one for handling response, but they are all combined into one because of the power and beauty of Task<T>.Basically in a delegating handler, if you need to inspect the request then you would simply do it in the SendAsync. However, if you need to work on the response, you would do it in the continuation of the Task.
So let's see some code. Let's imagine I need to write to the trace all incoming request URLs and add a custom response header identifying that this URL has been written to the trace (a very fictitious case, we will see some real world scenarios in the next post), we can code it very easily:
public class DummyHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // work on the request Trace.WriteLine(request.RequestUri.ToString()); return base.SendAsync(request, cancellationToken) .ContinueWith(task => { // work on the response var response = task.Result; response.Headers.Add("X-Dummy-Header", Guid.NewGuid().ToString()); return response; }); } }
Execution of message handlers in Web API pipeline
Until recently, you could only register MessageHandlers globally. ASP.NET team have recently added much awaited per-route message handler support announced only a few weeks ago.So as explained and illustrated by Henrik in the link above (and also by Kiran Challa in his nice diagram), execution of message handlers depends on whether they are registered globally or per-route. Global message handlers are executed before HttpRoutingDispatcher work while per-route message handlers are executed after it.
Registering a global message handler (DelegatingHandler)
Global message handlers (must be DelegatingHandlers) are a property of the configuration object. In a web hosted Web API:GlobalConfiguration.Configuration.MessageHandlers.Add( new DummyHandler());
As we discussed, the order of registering a handler is very important and defines the size of the Russian doll, higher it is in the list, more powerful it will be as it becomes the soonest receiving the request and last receiving the response (hence more flexibility and breadth of operation).
UPDATE 06/08/2012: The order of calling message handlers has changed for RTM release. So in RTM, first item is called first for request and last for response.
Registering a per-route message handler(s)
This is easy and is at the same time of registering the route itself. Here we register MyMessageHandler:IHttpRoute route = config.Routes.CreateRoute( routeTemplate: "api/MyRoute", defaults: new HttpRouteValueDictionary("route"), constraints: null, dataTokens: null, parameters: null, handler: new MyMessageHandler()); config.Routes.Add("MyRoute", route);
One might wonder, it would have been great if we could register a collection of delegating handlers along with the actual handler. In fact we can! Se let's say we have two DelegatingHandler implementations as RussianDoll_1 and RussianDoll_2. So we can now say:
var handler = new RussianDoll_1() { InnerHandler = new RussianDoll_2() { InnerHandler = new MyMessageHandler() } }; IHttpRoute route = config.Routes.CreateRoute( routeTemplate: "api/MyRoute", defaults: new HttpRouteValueDictionary("route"), constraints: null, dataTokens: null, parameters: null, handler: handler); config.Routes.Add("MyRoute", route);
Conclusion
In this post, we briefly reviewed the history of two types of HTTP pipeline components: the one that is responsible for processing a request and sending back a response, and the other, a mid-stream component in the HTTP pipeline sniffing the incoming and outgoing request/response which can sometimes intervene in the message processing.In brief, Web API heavily uses the abstraction of HttpMessageHandler and DelegatingHandler - latter inheriting from the former. Web API allows for registration of global message handlers or per route handlers. In the next post, we will review some of the real-world use cases of Message Handlers.
This series on ASP.NET Web API is among the most informative on the web.
ReplyDeleteA great diagram on Kiran Challa's blog.
Thanks. Yes, Kiran's diagram is in my browser bookmarks and I commonly refer to.
DeleteSo what else would you like to see in the series? What topic is interesting to you?
A topic on filtering would be really interesting.
Deleteplease post a topic on authenticating and authorization of web api using "token" approach which can be understandable to all. I have gone through some sites which are far too complex for me to understand
ReplyDeleteSounds like a good idea. OK, security will be it. Probably in a few weeks.
DeleteFantastic, thank you!!!
ReplyDeleteI am happy to find your way of writing the post. Now you make it easy for me to understand and implement the concept. Thank you for the post.
ReplyDeletevideo and blog marketing
this code won't work for post,put and delete request...?
ReplyDeletewhat should i do
Great post, thanks for sharing.
ReplyDeleteWeb Design company in Hubli | web designing in Hubli | SEO company in Hubli