skip to Main Content

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?

2

Answers


  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 the IServiceProvider. See CreateInstance(IServiceProvider, Type, Object[]) for details.

    So, here’s answer to your question:

    Typed client types are not retrieved directly from the IServiceProvider.

    Login or Signup to reply.
  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 DelegatingHandlers.

    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 an ITypedHttpClientFactory service
    • Thirdly we fetch the named client .CreateClient("test")
      • This client is decorated with the dummy DelegatingHandler
    • Finally we create a TypedClient instance via the ITypedHttpClientFactory by injecting the previously fetched named HttpClient

    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);
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search