HttpClient Message Handlers in ASP.NET Web API
by Mike Wasson
A message handler is a class that receives an HTTP request and returns an HTTP response.
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.
On the client side, the HttpClient class uses a message handler to process requests. The default handler is HttpClientHandler, which sends the request over the network and gets the response from the server. You can insert custom message handlers into the client pipeline:
[!NOTE] ASP.NET Web API also uses message handlers on the server side. For more information, see HTTP Message Handlers.
Custom Message Handlers
To write a custom message handler, derive from System.Net.Http.DelegatingHandler and override the SendAsync method. Here is the method signature:
[!code-csharpMain]
1: Task<HttpResponseMessage> SendAsync(
2: HttpRequestMessage request, CancellationToken cancellationToken);
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.
The following example shows a message handler that adds a custom header to the outgoing request:
[!code-csharpMain]
1: class MessageHandler1 : DelegatingHandler
2: {
3: private int _count = 0;
4:
5: protected override Task<HttpResponseMessage> SendAsync(
6: HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
7: {
8: System.Threading.Interlocked.Increment(ref _count);
9: request.Headers.Add("X-Custom-Header", _count.ToString());
10: return base.SendAsync(request, cancellationToken);
11: }
12: }
The call to base.SendAsync
is asynchronous. If the handler does any work after this call, use the await keyword to resume execution after the method completes. The following example shows a handler that logs error codes. The logging itself is not very interesting, but the example shows how to get at the response inside the handler.
[!code-csharpMain]
1: class LoggingHandler : DelegatingHandler
2: {
3: StreamWriter _writer;
4:
5: public LoggingHandler(Stream stream)
6: {
7: _writer = new StreamWriter(stream);
8: }
9:
10: protected override async Task<HttpResponseMessage> SendAsync(
11: HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
12: {
13: var response = await base.SendAsync(request, cancellationToken);
14:
15: if (!response.IsSuccessStatusCode)
16: {
17: _writer.WriteLine("{0}\t{1}\t{2}", request.RequestUri,
18: (int)response.StatusCode, response.Headers.Date);
19: }
20: return response;
21: }
22:
23: protected override void Dispose(bool disposing)
24: {
25: if (disposing)
26: {
27: _writer.Dispose();
28: }
29: base.Dispose(disposing);
30: }
31: }
Adding Message Handlers to the Client Pipeline
To add custom handlers to HttpClient, use the HttpClientFactory.Create method:
[!code-csharpMain]
1: HttpClient client = HttpClientFactory.Create(new Handler1(), new Handler2(), new Handler3());
Message handlers are called in the order that you pass them into the Create method. Because handlers are nested, the response message travels in the other direction. That is, the last handler is the first to get the response message.
|