21

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..

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Vinyl Warmth
  • 2,226
  • 3
  • 25
  • 50

4 Answers4

12

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!

mountain traveller
  • 7,591
  • 33
  • 38
  • I am using the same retry pattern and getting the exception as "Please use asynchronous-defined policies when calling asynchronous ExecuteAsync (and similar) methods.". I was breaking my head to understand what went wrong..and you saved me. Thank you..!! – Thangadurai Oct 12 '17 at 06:39
7

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.

Yusef Maali
  • 2,201
  • 2
  • 23
  • 29
1

I might be late to the game but I leave here a post for future readers. I have created two slightly different solutions for this refresh token with retry problem.

Retry policy, Delegating Handler and Custom exception

  • Here is sequence diagram which depicts the communication flow
  • Here is the full source code

refresh token in case of 401

Retry policy, Delegating Handler and Polly.Context

This version separates the responsibilities in a different way:

  • Here is sequence diagram which depicts the communication flow
  • Here is the full source code

refreshing token

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
-1

this is how async await work in .net, when the execution of your code reaches an await, two things will happened

  1. the current thread of your code should release to make your code async, that means, your method should return

  2. when the task you await is complete, your method should continue from where it used to be

Sean
  • 2,990
  • 1
  • 21
  • 31