Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Polly to retry after HttpStatusCode.Unauthorized

I'm making calls to an external API and want to deal with the event that a call returns an Unauthorized HttpResponseMessage. When this happens I want to refresh the access token and make the call again.

I'm trying to use Polly with the following code:

    public async Task<HttpResponseMessage> MakeGetRequestAsync()
    {
        var retryPolicy = Policy
            .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized)
            .Retry(1, (exception, retryCount) =>
            {
                RefreshAccessToken();
            });

        var result = await retryPolicy.ExecuteAsync(() => CallApiAsync());

        return result;
    }


    private async Task<HttpResponseMessage> CallApiAsync()
    {
        var url = Options.ResourceSandboxUrl;

        var httpClient = new HttpClient();
        SetRequestHeaders(httpClient);
        var response = await httpClient.GetAsync(url);

        response.StatusCode = HttpStatusCode.Unauthorized;
        return response;
    }

I put breakpoints on the ExecuteAsync statement and in DoSomethingAsync - when I step over ExecuteAsync DoSomethingAsync is not called and control is returned to the function that called MakeGetRequestAsync

I don't understand why DoSomethingAsync is not called - can anyone help me with what I'm trying to achieve?

I've looked at the Polly documentation & Polly questions on Stack Overflow but I can't figure out what's going on..

like image 663
Vinyl Warmth Avatar asked Jan 28 '17 13:01

Vinyl Warmth


2 Answers

To use ExecuteAsync() you must declare the policy as .RetryAsync(...), not .Retry(...).

If your actual code reads exactly as the code sample above, the .ExecuteAsync(...) will be throwing for the mismatch between .Retry(...) [a sync policy] and .ExecuteAsync(...) [an async execution]. Since this exception is thrown, CallApiAsync() is indeed never invoked. You should be able to see the thrown exception, when calling MakeGetRequestAsync()

Overall code approach looks good tho: this retry-refreshing-authentication is a proven pattern with Polly!

like image 139
mountain traveller Avatar answered Nov 16 '22 04:11

mountain traveller


I'm replying to this old question just to point out the Polly wiki page where this pattern was official documented:

retry-to-refresh-authorization

In particular this is the code snippet suggested:

var authorisationEnsuringPolicy = Policy
    .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized) 
    .RetryAsync(
       retryCount: 1, // Consider how many retries. If auth lapses and you have valid credentials, one should be enough; too many tries can cause some auth systems to blacklist. 
       onRetryAsync: async (outcome, retryNumber, context) => FooRefreshAuthorizationAsync(context), 
      /* more configuration */); 

var response = authorisationEnsuringPolicy.ExecuteAsync(context => DoSomethingThatRequiresAuthorization(context), cancellationToken);

The FooRefreshAuthorizationAsync(...) method can obtain a new authorization token and pass it to the delegate executed through the policy using Polly.Context.

like image 6
Yusef Maali Avatar answered Nov 16 '22 03:11

Yusef Maali