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);
}
}