Polly中结合瞬时故障捕获与重新授权的实现问题
解决Polly重试+401自动重认证的问题
我懂你这个痛点!Polly的策略组合确实容易在顺序和细节上踩坑,你的代码里有几个核心问题导致401重新认证后原请求没重试,咱们一步步来改:
问题分析
策略包裹顺序搞反了
你当前用WaitAndRetryPolicy.WrapAsync(ReAuthPolicy),意味着Polly会先执行重认证策略,再执行网络重试策略。但我们需要的逻辑是:先发起请求(带网络重试),如果返回401,再触发重认证,然后重新执行整个请求流程(包括网络重试)。顺序反了就会导致重认证后不会再走原请求的重试逻辑。重认证后没更新请求的Token
你的LogInWithSavedCredsAsync虽然执行成功,但如果没把新获取的JWT更新到_client的请求头里,下次请求还是用旧的过期Token,自然还是401,等于白重认证了。没处理重认证失败的场景
当前策略没考虑“重认证也返回401”的情况,这时候应该直接终止流程并登出用户,而不是继续重试。
修正后的完整代码
// 核心:调整策略包裹顺序,重认证策略在外层,包裹网络重试策略 public static PolicyWrap<HttpResponseMessage> RetryPolicy { get => ReAuthPolicy.WrapAsync(WaitAndRetryPolicy); } // 网络故障重试策略:只处理网络异常和非401的错误响应 private static IAsyncPolicy<HttpResponseMessage> WaitAndRetryPolicy { get => Policy .Handle<WebException>() .Or<HttpRequestException>() .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode && r.StatusCode != HttpStatusCode.Unauthorized) .WaitAndRetryAsync(4, _ => TimeSpan.FromSeconds(2), (exception, timespan, retryCount, context) => { // 可选:添加重试日志 Console.WriteLine($"网络请求失败,第{retryCount}次重试,间隔{timespan.TotalSeconds}秒"); }); } // 401重认证策略:处理未授权响应,重认证后更新Token,失败则登出 private static IAsyncPolicy<HttpResponseMessage> ReAuthPolicy { get => Policy .HandleResult<HttpResponseMessage>(x => x.StatusCode == HttpStatusCode.Unauthorized) .RetryAsync(1, async (responseDetails, retryCount, context) => { // 执行自动重认证 bool authSuccess = await App.CoreService.LogInWithSavedCredsAsync(true); if (!authSuccess) { // 重认证失败:登出用户并终止流程 await App.CoreService.LogOutAsync(); throw new UnauthorizedAccessException("保存的凭据已失效,已自动登出"); } // 关键步骤:更新HttpClient的Authorization头为新Token string newToken = App.CoreService.GetCurrentValidToken(); // 假设你有这个方法获取最新Token _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", newToken); }); }
代码说明
- 策略顺序修正:现在
ReAuthPolicy作为外层,当内层的WaitAndRetryPolicy执行完(包括4次网络重试)后,如果返回401,外层会触发重认证,然后重新执行整个内层流程(也就是重新发起原请求+网络重试)。 - 排除401的网络重试:让网络重试策略只处理真正的网络故障,把401的逻辑完全交给重认证策略,避免冲突。
- 更新请求Token:重认证成功后必须更新
HttpClient的请求头,确保后续请求用的是有效Token。 - 处理重认证失败:如果保存的凭据也失效了,直接登出用户并抛出异常,终止Polly流程,不会无意义地重试。
使用示例
try { HttpResponseMessage result = await RetryPolicy.ExecuteAsync(() => _client.SendAsync(request)); // 处理成功响应 result.EnsureSuccessStatusCode(); // 可选:把非成功响应转为异常 } catch (UnauthorizedAccessException ex) { // 处理登出后的逻辑,比如跳转到登录页面 } catch (Exception ex) { // 处理网络重试4次后仍然失败的情况 }
最后别忘了确保LogInWithSavedCredsAsync能正确返回认证结果,GetCurrentValidToken能拿到最新的JWT哦!
内容的提问来源于stack exchange,提问作者vaindil
相关产品推荐
相关产品推荐

