2

I have a class A which has a GetClient method that creates named-HttpClients using HttpClientFactory, like this:

public class A : AInterface {
    A (IHttpClientFactory factory, ...) {
        _httpClientFactory = factory;
    }

    private CustomClient GetClient (string httpClientName, Uri baseUri) {
        var client = _httpClientFactory.CreateClient(httpClientName);
        client.BaseAddress = baseUri;
        return new CustomClient (client);
    }
}

Here, CustomClient is autogenerated client-side code that sends the HTTP requests using the client that it was created with.

I want to add Polly retry/timeout policy for all HttpClients that are returned by GetClient() method.

What I tried

Added this in Startup.cs to inject HttpClientFactory as dependency:

   services.AddHttpClient<AInterface, A>()
       .AddPolicyHandler(PolicyHandler.RetryPolicy())
       .AddPolicyHandler(PolicyHandler.TimeoutPolicy());
   services.AddScoped<AInterface, A>();

My understanding was that all clients created by this HttpClientFactory will have the retry policy associated with it but this doesn't seem to work.

Any suggestions? What am I doing wrong? Thanks in advance!

Edit 1: I digged into this further.. Looks like the retry policy is not applied because GetClient() returns a named client whereas the retry policy was added to unnamed client at startup. However, I can't avoid using named HttpClient (because some authorization logic depends on it). But I also have no way of knowing all the possible names at Startup so I can't add policies to named clients at startup.. Appreciate any inputs!

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
sg1993
  • 335
  • 2
  • 19
  • Could you please elaborate on this: *However, I can't avoid using named HttpClient (because some authorization logic depends on it)* Why do you rely on this? – Peter Csala Feb 28 '21 at 12:21
  • There is some business logic to authentic based on the http-client name so if I remove the client name, it leads to authentication errors.. Is there any way to apply polly policies on the client that is returned by CreateClient(..)? That would have solved all my problems.. – sg1993 Mar 01 '21 at 02:22
  • Sorry but it is still unclear. Are you looking for a solution with named client or with typed client or a mixture of these two? Or something different? – Peter Csala Mar 01 '21 at 09:18
  • I am looking for a way to add polly policies to named httpclients but the catch is that I don't know the names during Startup; the names are decided at run-time. Using HttpClientFactory, I can add configuration to named clients but only if I know the names during Startup unless my understanding is wrong.. – sg1993 Mar 01 '21 at 16:49
  • Named client can be registered at Startup time and they can be retrieved via the `HttpClientFactory`'s `CreateClient`. According to my knowledge whenever the `ServiceCollection` is populated then the runtime will build from it a `ServiceProvider`. The `IServiceProvider` defines only a `GetService` ([1](https://learn.microsoft.com/en-us/dotnet/api/system.iserviceprovider.getservice)). – Peter Csala Mar 02 '21 at 07:27
  • That's why I don't get it why do you want to rely on the name of the HttpClient. It is a DI related concept, which should NOT be reused as a part of your business logic. – Peter Csala Mar 02 '21 at 07:28

1 Answers1

1

If you want to add a policy to all of the clients which are created via an IHttpClientFactory then you need to call ConfigureAll for HttpClientFactoryOptions:

services.ConfigureAll<HttpClientFactoryOptions>(options =>
{
    options.HttpMessageHandlerBuilderActions.Add(builder =>
    {
        var handler = ...
        builder.AdditionalHandlers.Add(handler);
    });
});

This solution will decorate all clients including those that are created via CreateClient without name or named clients or typed clients.

But what should be the handler? The AdditionalHandlers is an IList<DelegatingHandler> so, we need to add a DelegatingHandler. Fortunately the PolicyHttpMessageHandler is a DelegatingHandler (whenever you call AddPolicyHandler under the hood it creates such a handler).

var handler = new PolicyHttpMessageHandler(GetYourPolicy());

Please bear in mind in normal cases when you use directly the PolicyHttpMessageHandler you have to set its InnerHandler. But in this case you shouldn't otherwise you would receive an InvalidOperationException.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75