Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the full URI within a Polly retry attempt?

I'm using Polly to retry HttpClient attemnpts :

        services.AddHttpClient<JoinPackageApiClient>(jp => { jp.BaseAddress = new Uri(appSettings.JoinPackageWS.BaseUrl); })
            .AddPolicyHandler(GetRetryPolicy(appSettings, serviceProvider))

Where GetRetryPolicy is :

 private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(AppSettings appSettings, ServiceProvider serviceProvider)
        {
            return HttpPolicyExtensions
                .HandleTransientHttpError()
                .OrResult(msg => msg.StatusCode != HttpStatusCode.OK)
                .Or<TimeoutRejectedException>()
                .Or<TaskCanceledException>()
                .Or<OperationCanceledException>()

                .WaitAndRetryAsync(appSettings.PollySettings.RetryAttempts, (retryAttempt, c) =>
                {

                 return TimeSpan.FromSeconds(2);
                }, onRetry: (response, delay, retryCount, context) =>
                {

                   //█how can I access the full(!) HttpClient's URI here ?
                   //e.g. : https://a.com/b/c?d=1
               
                });
        }

Question:

Looking at the onRetry parameter, I want to log the full URL attempt in the onRetry section.

How can I get the full URI in that section ?

like image 349
Royi Namir Avatar asked Oct 23 '25 23:10

Royi Namir


1 Answers

I was wrong when I have stated in my comment that this is not possible.

enter image description here

I have to correct myself: Yes, it is doable and it is actually pretty easy. :)

All you need to do is to use a different overload of AddPolicyHandler

public static IHttpClientBuilder AddPolicyHandler (this IHttpClientBuilder builder, Func<IServiceProvider,HttpRequestMessage,IAsyncPolicy<HttpResponseMessage>> policySelector);

So, whenever you call the AddPolicyHandler you have to provide a function, which

  • receives an IServiceProvider instance to be able to access to any DI registered service
  • also receives the request on which you can access the url via the RequestUri property
  • and returns an IAsyncPolicy<HttpResponseMessage>

So, you have to modify the registration code like this:

services.AddHttpClient<JoinPackageApiClient>(jp => ...)
        .AddPolicyHandler((provider, request) => GetRetryPolicy(appSettings, provider, request));

and the GetRetryPolicy method like this:

private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(
    AppSettings appSettings,
    IServiceProvider serviceProvider, 
    HttpRequestMessage request)
   => HttpPolicyExtensions
        .HandleTransientHttpError()
        ...
        .WaitAndRetryAsync(appSettings.PollySettings.RetryAttempts, 
        (_, __) => TimeSpan.FromSeconds(2),
        (response, delay, retryCount, context) =>
        {
            var url = request.RequestUri;
            // ...
        });

The good news is that the url will change whenever you issue a new request. (In my test client I have set the BaseAddress to http://httpstat.us) When I run this test

await client.GetAsync("/200");
try
{
    await client.GetAsync("/401");
}
catch { }
await client.GetAsync("/403");

then the retry triggered 4 times (2x for 401 and 2x for 403). The onRetry received the related HttpRequestMessage so, the logging was accurate.

like image 187
Peter Csala Avatar answered Oct 26 '25 19:10

Peter Csala



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!