Mar*_*niz 11 c# dotnet-httpclient refit .net-core polly
我有一个应用程序请求经过身份验证的服务,需要通过access_token.
我的想法是在过期时使用 Polly 重试access_token。
我在 .NET Core 3.1 应用程序中使用 Refit (v5.1.67) 和 Polly (v7.2.1)。
服务注册如下:
services.AddTransient<ExampleDelegatingHandler>();
IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy<HttpResponseMessage>
.Handle<ApiException>()
.RetryAsync(1, (response, retryCount) =>
{
System.Diagnostics.Debug.WriteLine($"Polly Retry => Count: {retryCount}");
});
services.AddRefitClient<TwitterApi>()
.ConfigureHttpClient(c =>
{
c.BaseAddress = new Uri("https://api.twitter.com/");
})
.AddHttpMessageHandler<ExampleDelegatingHandler>()
.AddPolicyHandler((sp, req) =>
{
//this policy does not works, because the exception is not catched on
//"Microsoft.Extensions.Http.PolicyHttpMessageHandler" (DelegatingHandler)
return retryPolicy;
});
Run Code Online (Sandbox Code Playgroud)
public interface TwitterApi
{
[Get("/2/users")]
Task<string> GetUsers();
}
Run Code Online (Sandbox Code Playgroud)
public class ExampleDelegatingHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
return await base.SendAsync(request, cancellationToken);
}
catch (Exception)
{
//Why do not catch the exception?
throw;
}
}
}
Run Code Online (Sandbox Code Playgroud)
重试策略不起作用!
分析问题后,我意识到异常没有被捕获在 HttpClient 的DelegatingHandler. 由于该AddPolicyHandler语句生成一个DelegatingHandler( PolicyHttpMessageHandler) 来执行策略,并且该处没有捕获异常,因此该策略永远不会执行。我意识到问题只发生在可以发送请求的异步场景中。在同步场景中它可以工作(例如:超时)。
为什么异常没有被捕获在里面DelegatingHandler?
我附上了一个模拟 Twitter 调用的示例项目。
https://www.dropbox.com/s/q1797rq1pbjvcls/ConsoleApp2.zip?dl=0
外部参考:
https://github.com/reactiveui/refit#using-httpclientfactory
https://www.hanselman.com/blog/UsingASPNETCore21sHttpClientFactoryWithRefitsRESTLibrary.aspx
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1
And*_*iva 47
我遇到了涉及 .NET 5 >= Polly 和 HttpClient 的问题,编译器显示:HttpClientBuilder 不包含 AddPolicyHandler 的定义。当我将 Nuget 包更改Polly.Extensions.Http为时,我可以修复它Microsoft.Extensions.Http.Polly,我知道这与此处报告的情况不同,但对于像我一样来这里寻找答案的其他人可能有用。
Pet*_*ala 14
AddPolicyHandlerTL;DR:和的顺序AddHttpMessageHandler确实很重要。
我已经重现了这个问题HttpClient(所以没有改装)。
public interface ITestClient
{
Task<string> Get();
}
public class TestClient: ITestClient
{
private readonly HttpClient client;
public TestClient(HttpClient client)
{
this.client = client;
}
public async Task<string> Get()
{
var resp = await client.GetAsync("http://not-existing.site");
return "Finished";
}
}
Run Code Online (Sandbox Code Playgroud)
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private readonly ITestClient client;
public TestController(ITestClient client)
{
this.client = client;
}
[HttpGet]
public async Task<string> Get()
{
return await client.Get();
}
}
Run Code Online (Sandbox Code Playgroud)
public class TestHandler: DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
return await base.SendAsync(request, cancellationToken);
}
catch (System.Exception ex)
{
_ = ex;
throw;
}
}
}
Run Code Online (Sandbox Code Playgroud)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddTransient<TestHandler>();
services.AddHttpClient<ITestClient, TestClient>()
.AddHttpMessageHandler<TestHandler>() //Handler first
.AddPolicyHandler(RetryPolicy()); //Policy second
}
private IAsyncPolicy<HttpResponseMessage> RetryPolicy()
=> Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.RetryAsync(1, (resp, count) =>
{
Console.WriteLine(resp.Exception);
});
Run Code Online (Sandbox Code Playgroud)
TestController的GetTestClient的GetTestHandler的SendAsync的tryRetryPolicy的onRetryTestHandler的SendAsync的catchTestControllerGet失败并显示HttpRequestException(inner: SocketException)因此,这里不会触发重试策略。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddTransient<TestHandler>();
services.AddHttpClient<ITestClient, TestClient>()
.AddPolicyHandler(RetryPolicy()) //Policy first
.AddHttpMessageHandler<TestHandler>(); //Handler second
}
private IAsyncPolicy<HttpResponseMessage> RetryPolicy()
=> Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.RetryAsync(1, (resp, count) =>
{
Console.WriteLine(resp.Exception);
});
Run Code Online (Sandbox Code Playgroud)
TestController的GetTestClient的GetTestHandler的SendAsync的tryTestHandler的SendAsync的catchRetryPolicy的onRetryTestHandler的SendAsync的tryTestHandler的SendAsync的catchTestControllerGet失败并显示HttpRequestException(inner: SocketException)因此,这里重试政策已被解雇。
在执行策略和委派处理程序时,失败的 HTTP 响应也不是例外。这只是一个HttpResponseMessage不成功的例子。Refit 将此状态转换为异常,作为请求-响应处理的最后一步。
正如Peter Csala 的回答中正确指出的那样,顺序很重要。当提出请求时:
HttpRequestMessage并传递给HttpClientHttpClient做初步准备HttpClient按照添加到客户端的顺序通过处理程序和策略运行请求消息HttpResponseMessage对象HttpClient进行最终处理并将结果返回给RefitApiExceptions因此,重试策略将重新运行在其之后添加的所有内容,但在其之前的内容将仅执行一次。
因此,如果您希望access_token在每次重试时重新生成令牌,则必须在重试策略之后注册创建令牌的委托处理程序。
重试 HTTP 失败的最简单方法是使用HttpPolicyExtensions.HandleTransientHttpError()from Polly.Extensions.Http。否则,您必须自己检查所有失败的 HTTP 状态代码。这样做的好处HandleTransientHttpError是它只重试那些有意义的失败,例如 500 或套接字错误。另一方面,例如,它不会重试 404,因为资源不存在,并且如果我们重试,该资源不太可能重新出现。
| 归档时间: |
|
| 查看次数: |
24383 次 |
| 最近记录: |