I have some AuthorizationHandler that I register with a typed client:
var clientFactory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();
services.AddHttpClient<TypedClient>()
.AddHttpMessageHandler(() =>
{
var authorization = new BearerAuthorization(clientFactory, clientId, clientSecret, authHost, token);
return new AuthorizationHandler(authorization);
});
When I get the “TypedClient” directly through the DI container, everything works fine and the AuthorizationHandler handler is called before the request, for example, like this:
_typedClient = serviceProvider.GetRequiredService<TypedClient>();
await _typedClient.SendRequest(); // OK
BUT. When trying to get this client from the ITypedHttpClientFactory factory, the handler stops being called, which gives an authorization error:
_httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
_typedClientFactory = serviceProvider.GetRequiredService<ITypedHttpClientFactory<TypedClient>>();
using var client = _httpClientFactory.CreateClient();
using var typedClient = _typedClientFactory.CreateClient(client);
await typedClient.SendRequest(); // Fail
How do I get a typed client from the factory that will have the added handler called before the request?
1
This is expected, as the ITypedHttpClientFactory documentation states:
The default
ITypedHttpClientFactory<TClient>
uses type activation to create typed client instances. Typed client types are not retrieved directly from theIServiceProvider
. SeeCreateInstance(IServiceProvider, Type, Object[])
for details.
So, here’s answer to your question:
Typed client types are not retrieved directly from the
IServiceProvider
.
2
One way to solve this problem is to inject a named client into the typed client via the ITypedHttpClientFactory
‘s CreateClient
method. Whenever you fetch a named HttpClient
then it will retrieve it from the DI with all of its DelegatingHandler
s.
Here you can find a working example: https://dotnetfiddle.net/wC4Neg
Service registrations
ServiceCollection collection = new();
collection.AddTransient<TestDelegatingHandler>();
collection.AddHttpClient();
collection.AddHttpClient<ITypedClient, TypedClient>("test")
.AddHttpMessageHandler(sp => sp.GetService<TestDelegatingHandler>());
- Here we register a dummy
DelegatingHandler
into the DI container first - Then we register the default HttpClient factories
- Finally we register a typed client which is named
test
- Here we add the MessageHandler to the named client
Client creations
ServiceProvider provider = collection.BuildServiceProvider();
IHttpClientFactory namedFactory = provider.GetRequiredService<IHttpClientFactory>();
ITypedHttpClientFactory<TypedClient> typedFactory = provider.GetRequiredService<ITypedHttpClientFactory<TypedClient>>();
HttpClient namedClient = namedFactory.CreateClient("test");
TypedClient typedClient = typedFactory.CreateClient(namedClient);
- First we build the
ServiceCollection
- Then we retrieve an
IHttpClientFactory
and anITypedHttpClientFactory
service - Thirdly we fetch the named client
.CreateClient("test")
- This client is decorated with the dummy
DelegatingHandler
- This client is decorated with the dummy
- Finally we create a
TypedClient
instance via theITypedHttpClientFactory
by injecting the previously fetched namedHttpClient
Supporting structures
public interface ITypedClient
{
Task GetAsync();
}
public class TypedClient: ITypedClient
{
private readonly HttpClient _client;
public TypedClient(HttpClient client) => _client = client;
public async Task GetAsync() => await _client.GetAsync("http://stackoverflow.com");
}
public class TestDelegatingHandler: DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
"Hello".Dump();
return await base.SendAsync(request, cancellationToken);
}
}