The Polly retry library and the IHttpClientBuilder is a match made in heaven as it defines all the retry logic at startup. The actual HttpClient calls are therefore untouched by any retry code.
The retry logic is called policies, and they determine how and in what circumstances a retry must be done.
Retrying on HTTP timeouts (where the caller does not respond) differs slightly from other HTTP errors (where the caller returns 404 Not Found or 500 errors). This is because the HttpClient does not receive an response code, but throws a TimeoutRejectedException when the call time outs.
This requires your configuration to make a retry policy and wrap this policy in a timeout policy.
But enough talk, lets code.
STEP 1: THE NUGET PACKAGES
You need the following packages:
- Polly
- Microsoft.Extensions.Http.Polly
STEP 2: CONFIGURE IHttpClientBuilder AND POLLY POLICIES IN THE STARTUP
In the startup.cs, add a HttpClient to the services and configure the retry policies, and then wrap the retry policies in a timeout policy. This is an example from a startup.cs file:
public static IHostBuilder CreateHostBuilder(string[] args) { var host = Host.CreateDefaultBuilder(args); host.ConfigureServices((hostContext, services) => { // ... // ... services.AddHttpClient("HttpClient") .AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(5)); }); // ... // ... } return host; } private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() .Or<TimeoutRejectedException>() .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(30)); }
What’s happening here?
The services.AddHttpClient creates a new HttpClient.
The First policy handler added is the retry policy. Please note that the retry policy will also retry on TimeoutRejectedExceptions. This retry policy will retry 3 times with 30 seconds delay.
The next policy handler is the timeout handler. This handler will throw a TimeoutRejectedException when the url called have been unresponsive for 5 seconds.
STEP 3: USE THE IHttpClientFactory IN THE CALLING CLASS
There is no Polly code in the class that does the http calls:
namespace MyCode { public class MyClass { private readonly IHttpClientFactory _clientFactory; public MyClass(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public async Task<string> Get(string url) { string authUserName = "user"; string authPassword = "password"; var httpClient = _clientFactory.CreateClient("HttpClient"); // If you do not have basic authentication, you may skip these lines var authToken = Encoding.ASCII.GetBytes($"{authUserName}:{authPassword}"); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); // The actual Get method using (var result = await httpClient.GetAsync($"{url}")) { string content = await result.Content.ReadAsStringAsync(); return content; } } } }
The httpClient.GetAsync() will retry the call automatically if any of the conditions described in the GetRetryPolicy() occurs. It will only return after the call is either successful or the retry count is met.
FINAL NOTE: DO NOT SET HttpClient.Timeout
The HttpClient.Timeout will set the global timeout, i.e. the overall timeout, including polly retries. So if you set this timeout you will receive a TaskCanceledException or OperationCanceledException instead of the TimeoutRejectedException, and those exceptions cannot be caught by the timeout policy.
MORE TO READ:
- The Polly Project
- HttpClient: timeout-per-try versus timeout across whole operation from GitHub
- Implement timeout and retry policies for HttpClient in ASP NET Core with Polly by Kimserey
- Exploring the code behind IHttpClientFactory in depth by Andrew Lock
- Using C# HttpClient from Sync and Async code from briancaos
- HttpClient retry mechanism with .NET Core, Polly and IHttpClientFactory from briancaos