Polly政策无法使用"AddPolicyHandler"运行. [英] Polly policy not working using "AddPolicyHandler"

查看:94
本文介绍了Polly政策无法使用"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);
    });

执行顺序

  1. TestController Get
  2. TestClient Get
  3. TestHandler SendAsync try
  4. RetryPolicy onRetry
  5. TestHandler SendAsync catch
  6. TestController Get 失败,并出现 HttpRequestException (内部: SocketException )
  1. TestController's Get
  2. TestClient's Get
  3. TestHandler's SendAsync's try
  4. RetryPolicy's onRetry
  5. TestHandler's SendAsync's catch
  6. TestController's Get fails with HttpRequestException (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);
    });

执行顺序

  1. TestController Get
  2. TestClient Get
  3. TestHandler SendAsync try
  4. TestHandler SendAsync catch
  5. RetryPolicy onRetry
  6. TestHandler SendAsync try
  7. TestHandler SendAsync catch
  8. TestController Get 失败,并出现 HttpRequestException (内部: SocketException )
  1. TestController's Get
  2. TestClient's Get
  3. TestHandler's SendAsync's try
  4. TestHandler's SendAsync's catch
  5. RetryPolicy's onRetry
  6. TestHandler's SendAsync's try
  7. TestHandler's SendAsync's catch
  8. TestController's Get fails with HttpRequestException (inner: SocketException)

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

So, here the retry policy has been fired.

这篇关于Polly政策无法使用"AddPolicyHandler"运行.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆