Is there any way to add Polly retry policy to all HttpClients created from HttpClientFactory?

Viewed 510

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!

1 Answers

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.

Related