HTTP Message Handlers
HTTP message handlers are a fundamental building block in .NET for sending and receiving HTTP requests and responses. They allow you to create flexible and extensible HTTP communication pipelines.
Core Concepts
The primary classes involved are:
HttpMessageHandler
: The base abstract class for all HTTP message handlers.HttpClient
: A high-level class for sending HTTP requests. It uses a pipeline ofHttpMessageHandler
instances to process requests and responses.
HttpMessageHandler
Pipeline
HttpClient
creates a pipeline of HttpMessageHandler
s. Each handler in the pipeline can inspect, modify, or generate HTTP messages. The pipeline is established by setting the HttpClient.MessageHandler
property or by using a HttpClientFactory
.
Common Built-in Handlers
HttpClientHandler
: The default handler that manages cookies, handles redirects, and applies proxy settings.HttpRequestValidationHandler
: Used in ASP.NET to validate incoming HTTP requests.DelegatingHandler
: A base class for creating custom handlers that wrap other handlers, allowing you to add cross-cutting concerns like logging, authentication, or retry logic.
Creating Custom Handlers
To create a custom message handler, you typically inherit from DelegatingHandler
. You override the SendAsync
method to intercept the request and/or response.
Here's a basic example of a logging handler:
public class LoggingHandler : DelegatingHandler
{
public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Log the outgoing request
Console.WriteLine($"Request: {request.Method} {request.RequestUri}");
// Send the request to the inner handler
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// Log the incoming response
Console.WriteLine($"Response: {response.StatusCode}");
return response;
}
}
Using Handlers with HttpClient
You can compose handlers to build a powerful request pipeline. For example, to use the LoggingHandler
with a default HttpClientHandler
:
var loggingHandler = new LoggingHandler(new HttpClientHandler());
var httpClient = new HttpClient(loggingHandler);
var response = await httpClient.GetAsync("https://example.com");
Console.WriteLine($"Final Status: {response.StatusCode}");
HttpClientFactory
For managing the lifecycle of HttpClient
instances and configuring handlers, especially in ASP.NET Core applications, IHttpClientFactory
is the recommended approach. It provides:
- Centralized configuration of handlers.
- Automatic management of
HttpClient
lifetimes. - Resilience policies (e.g., retry policies) through Polly integration.
Example using HttpClientFactory
:
// In Startup.cs (or Program.cs for .NET 6+)
services.AddHttpClient("MyApiClient")
.AddHttpMessageHandler<LoggingHandler>();
// In your service
public class MyService
{
private readonly HttpClient _httpClient;
public MyService(IHttpClientFactory clientFactory)
{
_httpClient = clientFactory.CreateClient("MyApiClient");
}
public async Task DoSomethingAsync()
{
var response = await _httpClient.GetAsync("https://api.example.com/data");
response.EnsureSuccessStatusCode();
// ... process response
}
}
Key Scenarios for Message Handlers
- Authentication: Adding authentication headers.
- Logging: Recording request and response details.
- Retry Logic: Automatically retrying failed requests.
- Caching: Implementing caching for responses.
- Request/Response Transformation: Modifying request bodies or response headers.
- Proxying: Configuring custom proxy behavior.
HttpClient
and its associated handlers properly to avoid resource leaks. HttpClientFactory
handles this automatically.