I have an HTTP message handler named AddHeadersHandler, which extends System.Net.Http.DelegatingHandler and I need it to be added to all current and future HttpClient instances, including typed, named and non-named clients.
I know I can add a handler using .AddHttpMessageHandler<AddHeadersHandler>() for a specific client, but how do I add it to all clients?
// AddHeadersHandler.cs
public class AddHeadersHandler: DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.TryAddWithoutValidation("X-Correlation-Id", Guid.NewGuid().ToString());
return base.SendAsync(request, cancellationToken);
}
}
// Startup.cs
services
.AddHttpContextAccessor()
.AddTransient<AddHeadersHandler>();
services
.AddHttpClient<MyClient>()
.AddHttpMessageHandler<AddHeadersHandler>(); // I don't want to specify this for each client.
// MyClient.cs
public class MyClient
{
public HttpClient HttpClient { get; }
public MyClient(HttpClient httpClient)
{
HttpClient = httpClient;
}
public async Task GetTest()
{
await HttpClient.GetAsync("https://localhost:5001/test"); // This should have headers attached.
}
}
It can be done by configuring HttpClientFactoryOptions for all named options. We need to provide a delegate in HttpMessageHandlerBuilderActions, which will include your handler to AdditionalHandlers property list.
There are multiple ways of doing this using the Options pattern:
.AddSingleton() ❌If your handler has any dependencies (e.g. IHttpContextAccessor to get current correlation id), we would like to use dependency injection to resolve it.
We could use the OptionsBuilder API to get a required handler using dependency injection. Unfortunately, this API does not provide a method to configure options for all named instances like .ConfigureAll does.
Luckily, we can get what we need by registering a factory method for IConfigureOptions<HttpClientFactoryOptions> like so:
// Startup.cs
services.AddSingleton<IConfigureOptions<HttpClientFactoryOptions>>(
provider =>
{
// When name is null, these options will be used for all clients.
return new ConfigureNamedOptions<HttpClientFactoryOptions>(
name: null,
options =>
{
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
// Here we have access to ServiceProvider
// to get an instance of the handler.
builder.AdditionalHandlers.Add(
provider.GetRequiredService<AddHeadersHandler>());
});
});
});
.ConfigureAll() ✔️The following improved answer was inspired by LostInComputer.
Add .ConfigureAll in your Startup.cs and use IServiceProvider through the builder like so:
services.ConfigureAll<HttpClientFactoryOptions>(options =>
{
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
builder.AdditionalHandlers.Add(builder.Services
.GetRequiredService<AddHeadersHandler>());
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With