Polly政策无法使用"AddPolicyHandler"运行. [英] Polly policy not working using "AddPolicyHandler"
问题描述
我有一个请求认证服务的应用程序,需要传递 access_token
.
I have an application that makes a request for an authenticated service, where it is necessary to pass the access_token
.
我的想法是,如果 access_token
过期,请使用Polly重试.
My idea is to use Polly to retry if the access_token
is expired.
我正在.NET Core 3.1应用程序中使用Refit(v5.1.67)和Polly(v7.2.1).
I'm using Refit (v5.1.67) and Polly (v7.2.1) in a .NET Core 3.1 application.
服务注册如下:
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;
});
public interface TwitterApi
{
[Get("/2/users")]
Task<string> GetUsers();
}
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;
}
}
}
重试策略不起作用!
分析问题后,我意识到HttpClient的 DelegatingHandler
中没有捕获异常.由于 AddPolicyHandler
语句正在生成 DelegatingHandler
( PolicyHttpMessageHandler
)来执行策略,并且未捕获异常,因此该策略永远不会执行.我意识到问题仅出现在可以发送请求的异步方案中.在同步方案中,它可以工作(例如:超时).
Analyzing the problem, I realized that the exception is not being caught inside the HttpClient's DelegatingHandler
. Since the AddPolicyHandler
statement is generating a DelegatingHandler
(PolicyHttpMessageHandler
) to execute the policy and the exception is not caught there, the policy never executes. I realized that the problem only occurs in asynchronous scenarios, where the request can be sent. In synchronous scenarios it works (example: timeout).
为什么未在 DelegatingHandler
中捕获异常???
Why the exception is not caught inside DelegatingHandler
??
我正在附上一个模拟Twitter呼叫的示例项目.
I am attaching an example project simulating a Twitter call.
https://www.dropbox.com/s/q1797rq1pbjvcls/ConsoleApp2.zip?dl=0
外部参考:
https://github.com/reactiveui/refit#using-httpclientfactory
https://www.hanselman.com/blog/UsingASPNETCoreCore21sHttpClientFactoryWithRefitsRESTLibrary.aspx
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view = aspnetcore-3.1
推荐答案
TL; DR: AddPolicyHandler
和 AddHttpMessageHandler
的顺序确实很重要.
TL;DR: The ordering of AddPolicyHandler
and AddHttpMessageHandler
does matter.
我已经用 HttpClient
重新创建了问题(因此没有Refit).
I've recreated the problem with HttpClient
(so without Refit).
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";
}
}
测试控制器
[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();
}
}
用于测试的DelegateHandler
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;
}
}
}
订购#1-处理程序,策略
启动
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);
});
执行顺序
-
TestController
的Get
-
TestClient
的Get
-
TestHandler
的SendAsync
的try
-
RetryPolicy
的onRetry
-
TestHandler
的SendAsync
的catch
-
TestController
的Get
失败,并出现HttpRequestException
(内部:SocketException
)
TestController
'sGet
TestClient
'sGet
TestHandler
'sSendAsync
'stry
RetryPolicy
'sonRetry
TestHandler
'sSendAsync
'scatch
TestController
'sGet
fails withHttpRequestException
(inner:SocketException
)
因此,此处的重试策略不会触发.
So, here the retry policy does not fired.
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);
});
执行顺序
-
TestController
的Get
-
TestClient
的Get
-
TestHandler
的SendAsync
的try
-
TestHandler
的SendAsync
的catch
-
RetryPolicy
的onRetry
-
TestHandler
的SendAsync
的try
-
TestHandler
的SendAsync
的catch
-
TestController
的Get
失败,并出现HttpRequestException
(内部:SocketException
)
TestController
'sGet
TestClient
'sGet
TestHandler
'sSendAsync
'stry
TestHandler
'sSendAsync
'scatch
RetryPolicy
'sonRetry
TestHandler
'sSendAsync
'stry
TestHandler
'sSendAsync
'scatch
TestController
'sGet
fails withHttpRequestException
(inner:SocketException
)
因此,此处的重试策略已被触发.
So, here the retry policy has been fired.
这篇关于Polly政策无法使用"AddPolicyHandler"运行.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!