Polly策略无法使用“ AddPolicyHandler”运行

时间:2020-08-18 01:36:41

标签: dotnet-httpclient polly refit

我有一个应用程序,它请求通过身份验证的服务,因此需要传递access_token

我的想法是在access_token过期时使用Polly重试。

我正在.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;
    });
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语句正在生成DelegatingHandlerPolicyHttpMessageHandler)来执行策略,并且没有捕获异常,因此该策略从不执行。我意识到问题仅出现在可以发送请求的异步方案中。在同步方案中,它可以工作(例如:超时)。

为什么未在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://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1

1 个答案:

答案 0 :(得分:2)

TL; DR:AddPolicyHandlerAddHttpMessageHandler的顺序确实很重要。


我已经用HttpClient重新创建了问题(因此没有重新安装)。

键入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";
    }
}

测试控制器

[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);
    });

执行顺序

  1. TestController的{​​{1}}
  2. Get的{​​{1}}
  3. TestClient的{​​{1}}的{​​{1}}
  4. Get的{​​{1}}
  5. TestHandler的{​​{1}}的{​​{1}}
  6. SendAsync的{​​{1}}失败,try(内部:RetryPolicy

因此,此处不会触发重试策略。

订购#2-策略,处理程序

启动

onRetry

执行顺序

  1. TestHandler的{​​{1}}
  2. SendAsync的{​​{1}}
  3. catch的{​​{1}}的{​​{1}}
  4. TestController的{​​{1}}的{​​{1}}
  5. Get的{​​{1}}
  6. HttpRequestException的{​​{1}}的{​​{1}}
  7. SocketException的{​​{1}}的{​​{1}}
  8. 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); }); 的{​​{1}}失败,出现TestController(内部:Get

因此,此处的重试策略已被触发。