使用Polly在HttpStatusCode.Unauthorized后进行重试

21

我正在调用一个外部API,希望处理一个调用返回Unauthorized HttpResponseMessage的事件。当这种情况发生时,我想刷新访问令牌并再次进行调用。

我尝试使用以下代码:Polly

    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;
    }
我在ExecuteAsync语句和DoSomethingAsync上设置了断点,当我跳过ExecuteAsync时,DoSomethingAsync没有被调用,控制权被返回给调用MakeGetRequestAsync的函数。我不明白为什么DoSomethingAsync没有被调用-有人能帮我理解一下吗?我查看了Polly文档和Stack Overflow上的Polly问题,但我无法弄清楚发生了什么。

只是想澄清一下:你说的“DoSomethingAsync”是指“RefreshAccessToken”吗? - Peter Csala
以上代码中DoSomethingAsync在哪里? - Zeeshan Badshah
4个回答

12
为了使用ExecuteAsync(),您必须将策略声明为.RetryAsync(...)而不是.Retry(...)
如果实际代码与上面的代码示例完全相同,则.ExecuteAsync(...)会因.Retry(...)[同步策略]和.ExecuteAsync(...)[异步执行]之间的不匹配而抛出异常。由于抛出了此异常,因此确实未调用CallApiAsync()。在调用MakeGetRequestAsync()时,您应该能够看到抛出的异常。
总体代码方法看起来很好: 这个使用Polly的重试-刷新身份验证是一个已证明的模式!

我正在使用相同的重试模式并得到异常提示“在调用异步ExecuteAsync(和类似方法)时,请使用已定义的异步策略。” 我一直在努力弄清楚哪里出了问题...而你救了我。谢谢..!! - Thangadurai

8
我只是回复这个旧问题,指出 Polly 的维基页面,其中介绍了这种模式的官方文档: retry-to-refresh-authorization 特别是建议使用以下代码片段:
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);

FooRefreshAuthorizationAsync(...) 方法可以获取新的授权令牌,并使用 Polly.Context 将其传递给通过策略执行的委托。


1

我可能晚了一步,但我在这里留下了一篇文章供未来读者参考。我已经为这个刷新令牌和重试问题创建了两个稍微不同的解决方案。

重试策略、委托处理程序和自定义异常

401情况下的刷新令牌

重试策略、委托处理程序和Polly.Context

这个版本以不同的方式分离了职责:

刷新令牌


-1

这是关于 .net 中异步等待的工作原理,当您的代码执行到 await 时,会发生两件事情:

  1. 您的代码当前线程应该释放以使您的代码异步化,也就是说,您的方法应该返回。

  2. 当您等待的任务完成后,您的方法应该从之前的位置继续执行。


谢谢,我该如何编写代码以实现我的要求? - Vinyl Warmth
1
所有这些关于异步执行的内容都是真实的,但它们都与用户调用CallApiAsync()未通过Polly进行调用无关。 - mountain traveller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接