10

Is there a way to add handlers to the default HTTP client in ASP.NET Core? Something like this?

.AddHttpClient()
.AddHttpMessageHandler<Handler1>()
.AddHttpMessageHandler<Handler2>();
janw
  • 8,758
  • 11
  • 40
  • 62
Shags
  • 133
  • 1
  • 1
  • 7
  • You can add an extension method on `IServiceCollection` called `AddHttpClient` that maybe takes a `DelegatingHandler` and then `services.AddSingleton(ctx => new System.Net.Http.HttpClient(handler));`. The issue is you can only add a handler in `HttpClient` constuctor. – JohanP Aug 01 '18 at 22:13
  • Indeed, remember that using a singleton instead of the IHttpClientFactory is not the recommended way for many applications (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0). But as always, it depends on your use case and your code base. – Kewin Remy Feb 26 '21 at 14:25

2 Answers2

28

Documentation states that you can only add handlers or configure the inner most handler to named or typed clients.

Reference Configure the HttpMessageHandler

It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.

An IHttpClientBuilder is returned when adding named or typed clients. The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate. The delegate is used to create and configure the primary HttpMessageHandler used by that client:

services.AddTransient<Handler1>();
services.AddTransient<Handler2>();

services.AddHttpClient("configured-inner-handler")
    .AddHttpMessageHandler<Handler1>()
    .AddHttpMessageHandler<Handler2>();
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            AllowAutoRedirect = false,
            UseDefaultCredentials = true
        };
    });
Nkosi
  • 235,767
  • 35
  • 427
  • 472
15

Upon inspecting the source code of DefaultHttpClientFactory (which is both the IHttpClientFactory and the IHttpMessageHandlerFactory registered by AddHttpClient method), it turns out that there is no use registering a custom IHttpMessageHandlerFactory because DefaultHttpClientFactory never requires it (but directly uses its own method). Of course we could also register a custom IHttpClientFactory, but there is a easier way to achieve what we want.

The idea is that DefaultHttpClientFactory calls the transient service HttpMessageHandlerBuilder during its IHttpMessageHandlerFactory implementation, so all we have to do is to register a custom HttpMessageHandlerBuilder. For example:

public class CustomHttpMessageHandlerBuilder : HttpMessageHandlerBuilder {
    public override string Name { get; set; }
    public override HttpMessageHandler PrimaryHandler { get; set; }
    public override IList<DelegatingHandler> AdditionalHandlers => new List<DelegatingHandler>();
    // Our custom builder doesn't care about any of the above.
    public override HttpMessageHandler Build() {
        return new HttpClientHandler {
            // Our custom settings
        };
    }
}

And then register it:

services.AddTransient<HttpMessageHandlerBuilder, CustomHttpMessageHandlerBuilder>();

And it works.

janw
  • 8,758
  • 11
  • 40
  • 62
Mu-Tsun Tsai
  • 2,348
  • 1
  • 12
  • 25