Few weeks back I received a task that sounded fairly simple but that also put me through a certain thought process. To set everything up, let’s just assume that we have an API called “A”. And we have another API called “B”. Theoretically, these APIs are totally independent and responsible for fairly different things. However, one of the end customers needed to call API A and receive the response from the API B. Sounds fairly easy. All we need to o is to listen to requests on a certain path on the API A and simply make the necessary call to API B and return the response when needed. As a background, both projects are ASP.NET APIs (not ASP.NET Core).
Middleware would have been my very first go to if we’d deal with an ASP.NET Core project. But for a regular ASP.NET Framework project this task seems to be more complicated than I anticipated. If only there was a way to achieve this in ASP.NET Framework! Or maybe there is one?
During the lunch break I was talking with some colleagues about Ocelot and how useful that library is. The discussion also went into the direction on how to extend Ocelot using own middleware or by just hooking into the Ocelot delegating handlers pipeline. That was the moment when my eyes probably started to shine. Delegating handlers are perfect for this type of scenario where you simply want to proxy requests to another mock api.
Delegating handlers
A 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.
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:
Just as a side note: the HttpClient also has its own message handlers. This means that you can similarly extend the HttpClient message handler pipeline by adding your own custom handlers, but this is out of the scope of this article.
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:
- Process the request message.
- Call base.SendAsync to send the request to the inner handler.
- The inner handler returns a response message. (This step is asynchronous.)
- 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.
A possible implementation
Here’s a stripped of version of the SendAsync method I used:
1 2 3 4 5 | 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);
}
This delegating handler just computes the URI of the remote REST resource and then we directly return the response instead of invoking base.SendAsync(). I did this because, as mentioned earlier, if this handler gets hit, then nothing else in the pipeline is of interest anymore. Also we don’t want the request to continue and try to resolve a controller, an action so on and so forth.
The only question that remains is however, how do I get to only use this delegating handler if a request for a certain URL comes in?
Delegating handler types
When it comes to delegating handlers in ASP.NET we have two different types: global handlers or route level handlers. Actually they are really the same syntactically! The only difference between these delegating handler types is decided by how the handlers are registered. If we register a handler using, for instance, config.MessageHandlers.Add(new AuthorizationHandler()); then this would be a global delegating handler and it would apply to all incoming requests.
On the other hand we can add handlers only to specific routes. Here’s a stripped of version of the route configuration for my custom delegating handler:
1 2 3 4 5 6 | var myApiRoute = config.Routes.CreateRoute( $"api/{RemoteApi.Route}/{{version}}/{{controller}}/{{id}}/{{user}}", new HttpRouteValueDictionary(new {id = RouteParameter.Optional, user = RouteParameter.Optional}), null, null, new MyCustomHandler(GlobalSettings.BaseUrl)); |
In this case, my custom handler would only be invoke when a request for this route comes in. Therefore, in API A I exposed a route dedicated to this purpose. When a client makes a call to this route, my custom handler will be executed, it will call the remote REST resource and return the response to the requester. Plain and simple.
Delegating handlers vs. middleware
In ASP.NET Core I could have achieved the same result using middleware. By registering a custom middleware using the app.Map() delegate that would call the remote REST resource would yield the same result. In my opinion, the concept of middleware in ASP.NET Core replaces the need to use delegating handlers to achieve tasks similar to the one described in this article. Delegating handlers are however still there also in ASP.NET Core. So what’s the point?
In my opinion, when it comes to Asp.Net Core delegating handlers are important when working with the HttpClient / HttpClientFactory. Steve Gordon has an excellent article series on HttpClientFactory and one of the articles describes exactly how to use delegating handlers. I strongly recommend you to read the entire series.
To just sum up, in ASP.Net Core I would use middleware to implement the functionality described in the intro but I would use delegating handlers to manage cross-cutting concerns when using the HttpClient via HttpClientFactory.
How useful was this post?
Click on a star to rate it!
Average rating / 5. Vote count:
Dan Patrascu-Baba
Latest posts by Dan Patrascu-Baba (see all)
- Configuration and environments in ASP.NET Core - 25/11/2019
- GraphQL in the .NET ecosystem - 19/11/2019
- A common use case of delegating handlers in ASP.NET API - 12/11/2019
Thanks for sharing your knowledge and experiences!