1

In my Startup.cs I have the following lines of code:

services.AddHttpClient<CustomClientFactory>()
        .AddTransientHttpErrorPolicy(
            p => p.WaitAndRetryAsync(3, 
                retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 1)));

This corresponds to a class I have which makes calls to an external service however it is not retrying upon failure. I have Polly installed. I'm not getting any errors it's simply not retrying if there is a failure. Am I going about it incorrectly?

Below is partial code for the CustomClientFactory:

public class CustomClientFactory: ICustomClientFactory
{

    private static readonly HttpClient _httpClient;

    private IConfiguration _config;

    public CustomClientFactory(IConfiguration config)
    {
        _config = config;
    }

   // other methods
}
3
  • Are you sure that it is not retrying? The above policy will only trigger if there was an HttpRequestException or the status code is either 408 or 5xx. Are you sure that one of these conditions are met? Commented Oct 9, 2021 at 7:26
  • @PeterCsala when I use postman to make a request to one of the API endpoints I created I get status code 500. However, this takes less than 500ms instead of the expected 14s. Do I have to make any changes to CustomClientFactory for this to work? Commented Oct 9, 2021 at 19:54
  • 1
    Your CustomClientFactory looks a bit weird. If you register a typed HttpClient then you should receive the HttpClient instance through the constructor. How do you receive that instance? Commented Oct 10, 2021 at 9:41

1 Answer 1

3

First, the AddTransientHttpErrorPolicy has been preconfigured errors to handle errors in the following categories: Network failures, http 5XX and Http 408 status code.

enter image description here

And since this Policy was configured to the CustomClientFactory, only the http request which is sent from the CustomClientFactory will use this policy, it means that if you directly using Postman to access the external service, without via the CustomClientFactory, it will not trigger this policy.

You can refer the following sample:

Prerequisites: Install the Microsoft.Extensions.Http.Polly package

Create a WeatherForecastController controller: In this controller, since there have two get method with the same route, it will show the 500 error.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
    [HttpGet]
    public IEnumerable<string> Get(int id)
    {
        return new string[] { "value1", "value2" };
    }
}

Then Create a TestClientFactory with the following code:

public class TestClientFactory
{
    private readonly HttpClient _httpClient;
    public TestClientFactory(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<List<WeatherForecast>> GetWeather()
    {
        _httpClient.BaseAddress = new Uri("https://localhost:5001"); 
        return await _httpClient.GetFromJsonAsync<List<WeatherForecast>>("WeatherForecast");
    }
}

And configure the retry policy in the ConfigureService method:

    public void ConfigureServices(IServiceCollection services)
    { 
        services.AddControllersWithViews();

        services.AddHttpClient<TestClientFactory>()
        .SetHandlerLifetime(TimeSpan.FromMinutes(5))
        .AddTransientHttpErrorPolicy(
        p => p.WaitAndRetryAsync(3,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 1)));
        //you can also use the following method.
        //.AddPolicyHandler(GetRetryPolicy()); 
    }

    //private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    //{
    //    return HttpPolicyExtensions
    //    .HandleTransientHttpError()
    //    .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(10));
    //}

Then, create a Test API controller to use the TestClientFactory:

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    private readonly TestClientFactory _testClient;
    public TestController(TestClientFactory testClient)
    {
        _testClient = testClient;
    }

    [HttpGet]
    [Route("weather")]
    public async Task<IActionResult> GetWeatherAsync()
    {
        return Ok(await _testClient.GetWeather());
    }
}

After running the application, we can see that: When using the postman directly access the WeatherForecastController, the result as below:

enter image description here

When using the postman access the WeatherForecastController via the CustomClientFactory, the result like this:

enter image description here

We can see that the retry policy works and it try multiple times.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.