Home > OS >  DI named custom HttpClient
DI named custom HttpClient

Time:08-06

I have a custom http client CustomHttpClient

I have to use IHttpClientFactory as azure auth, which I am trying to get to work, needs .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>()

How can I ensure that the named client I am adding with AddHttpClient is CustomHttpClient not just HttpClient

I tried

builder.Services.AddHttpClient<CustomHttpClient>("xxx", client =>
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddScoped<CustomHttpClient>(sp => (CustomHttpClient)sp.GetRequiredService<IHttpClientFactory>().CreateClient("xxx"));

but the .NET doesn't like the cast in the AddScoped

CodePudding user response:

I think there's a bit of misunderstanding on how to use IHttpClientFactory.

IHttpClientFactory always create HttpClient and never create TClient which is specified as the type argument of AddHttpClient method.

The TClient class is where you can inject the configured client. The Microsoft's documentation says,

A typed client accepts an HttpClient parameter in its constructor

So, configuring services like:

builder.Services.AddHttpClient<CustomHttpClient>(client =>
{
    client.BaseAddress = new Uri("http://custom.api.com");
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddHttpClient<AnotherHttpClient>(client =>
{
    client.BaseAddress = new Uri("http://another.api.com");
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

then, we can implement the CustomHttpClient class like:

public class CustomHttpClient
{
    private readonly HttpClient _client;

    public CustomHttpClient(HttpClient client)
    {
        _client = client; //<-- This client has base URL "http://custom.api.com"
    }

    public Task<string> GetSomething(string apiPath)
    {
        return _client.GetStringAsync(apiPath);
    }
}

If you still want to create HttpClient by name, you can give a name when calling AddHttpClient method as you did, and inject ITypedHttpClientFactory<CustomHttpClient> to CustomHttpClient and create HttpClient from it with name. But in such case, I don't see the reason to use ITypedHttpClientFactory. maybe, IHttpClientFactory is the choice.

CodePudding user response:

Since your CustomHttpClient class derives from HttpClient you cannot use IHttpClientFactory directly. However you can build your own factory and produce similar behavior by using IHttpMessageHandlerFactory

public class CustomHttpClient : HttpClient
{
    public CustomHttpClient(HttpMessageHandler handler, bool disposeHandler)
        : base(handler, disposeHandler)
    {
    }

    // ... your code 
}

public class CustomHttpClientFactory
{
    private readonly IHttpMessageHandlerFactory _handlerFactory;

    public CustomHttpClientFactory(IHttpMessageHandlerFactory handlerFactory)
    {
        _handlerFactory = handlerFactory;
    }

    public CustomHttpClient CreateClient(string name)
    {
        var handler = _handlerFactory.CreateHandler(name);
        return new CustomHttpClient(handler, disposeHandler: false);
    }
}

Edit: I was wrong where I assumed your CustomHttpClient does not derive from ´HttpClient` like described in the Docs

CustomHttpClient does not inherit from the HttpClient class. So the cast would always fail.

If you do not need to have multiple different named instances of CustomHttpClient all you need is AddHttpClient<Type>() and you can consume CustomHttpClient directly via DI.

In case you need multiple typed, named instances you can use the ITypedHttpClientFactory approach described in the answer of HttpClientFactory - Get a named, typed client by its name and build your own Factory class like this

public class CustomHttpClientFactory
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ITypedHttpClientFactory<CustomHttpClient> _typedClientFactory;

    public CustomHttpClientFactory(
        IHttpClientFactory httpClient,
        ITypedHttpClientFactory<CustomHttpClient> typedClientFactory)
    {
        _httpClientFactory = httpClient;
        _typedClientFactory = typedClientFactory;
    }

    public CustomHttpClient CreateClient(string name)
    {
        var httpClient = _httpClientFactory.CreateClient(name);
        return _typedClientFactory.CreateClient(httpClient);
    }
}
  • Related