6

目前我有这个要求:

await url
    .SetQueryParams(queryString)
    .SetClaimsToken()
    .GetJsonAsync<T>()

我现在想开始使用 Polly ( https://github.com/App-vNext/Polly ) 来处理重试并提供更好的用户体验。例如,由于网络连接不良,第一次尝试时不会“挂断”用户。这是我尝试使用的示例:

int[] httpStatusCodesWorthRetrying = { 408, 500, 502, 503, 504 };
Policy
    .Handle<HttpException>()
    .OrResult<HttpResponse>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
    .WaitAndRetryAsync(new[] {
                    TimeSpan.FromSeconds(1),
                    TimeSpan.FromSeconds(2),
                    TimeSpan.FromSeconds(3)
                })
    .ExecuteAsync( await url... )

但它必须HttpResponse是返回类型。从我的 Flurl 示例中可以看出,它正在返回T,即使它是一个HttpResponse. T只是用于反序列化的类型StringContent

这第一个示例根本不起作用,因为我在 PCL 中使用它并且我无法获得对System.Web那里的引用。所以我尝试了这个:

Policy
    .HandleResult(HttpStatusCode.InternalServerError)
    .OrResult(HttpStatusCode.BadGateway)
    .OrResult(HttpStatusCode.BadRequest)
    .WaitAndRetryAsync(new[] {
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(3)
    })
    .ExecuteAsync(async () =>
    {
        await url...
    });

但是这个也不起作用,因为 Polly 期望HttpStatusCode作为返回类型。所以我的问题是:我怎样才能告诉 polly 处理那些HttpStatusCodes 并且仍然允许我返回 type T

4

3 回答 3

18

您不需要中断使用诸如 之类的便利方法GetJsonAsync<T>(),因为 Flurl 会在非 2XX 响应(或者无论您如何配置)上抛出异常,这应该允许它与 Polly 很好地配合使用。只需删除原始代码中的.Handle<HttpException>and部分并改为处理:.OrResult<HttpResponse>FlurlHttpException

T poco = await Policy
    .Handle<FlurlHttpException>(ex => httpStatusCodesWorthRetrying.Contains((int)ex.Call.Response.StatusCode))
    .WaitAndRetryAsync(...)
    .ExecuteAsync(() => url
        .SetQueryParams(queryString)
        .SetClaimsToken()
        .GetJsonAsync<T>());

只是一个进一步清理的建议:

T poco = await Policy
    .Handle<FlurlHttpException>(IsWorthRetrying)
    .WaitAndRetryAsync(...)
    .ExecuteAsync(() => url
        .SetQueryParams(queryString)
        .SetClaimsToken()
        .GetJsonAsync<T>());

private bool IsWorthRetrying(FlurlHttpException ex) {
    switch ((int)ex.Call.Response.StatusCode) {
        case 408:
        case 500:
        case 502:
        case 504:
            return true;
        default:
            return false;
    }
}
于 2016-11-23T16:20:23.883 回答
10

Polly 可以将通过策略执行的委托返回的任何值解释为错误。但是,正如您所观察到的.GetJsonAsync<T>(),您发布的示例中的调用:

await url
    .SetQueryParams(queryString)
    .SetClaimsToken()
    .GetJsonAsync<T>()

正在回归THttpResponseMessage通过直接将 Json 反序列化到T.

您需要在 flurl 中使用重载,它会返回HttpResponseMessage. 我没用过flurl,但是这个重载返回Task<HttpResponseMessage>看起来很有希望。您可能可以执行以下操作:

List<int> httpStatusCodesWorthRetrying = new List<int>(new[] {408, 500, 502, 503, 504});
HttpResponseMessage response = await Policy
    .Handle<HttpRequestException>() 
    .Or<OtherExceptions>() // add other exceptions if you find your call may throw them, eg FlurlHttpException
    .OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains((int)r.StatusCode))
    .WaitAndRetryAsync(new[] {
                    TimeSpan.FromSeconds(1),
                    TimeSpan.FromSeconds(2),
                    TimeSpan.FromSeconds(3)
                })
    .ExecuteAsync(() => 
       url
        .SetQueryParams(queryString)
        .SetClaimsToken()
        .GetAsync()
    );

T responseAsT = await Task.FromResult(response).ReceiveJson<T>();

建议最后的调用.ReceiveJson<T>()只需将您的原始调用的 flurl 源代码与.GetJsonAsync<T>() 此处替换.GetAsync(); 源代码进行比较。

当然,您可以将其全部包装到 flurl 上的一个简洁的扩展辅助方法中,可能是这样的:

async T GetJsonAsyncResiliently<T>(this IFlurlClient client, Policy policy) // OR (if preferred): this Url url instead of IFlurlClient client
{
    return await Task.FromResult(policy.ExecuteAsync(() => client.GetAsync())).ReceiveJson<T>();
}

编辑:在指向IFlurlClient. 但是,在 flurl onUrl和中存在一组并行的扩展方法string,因此适用相同的原则。

于 2016-11-22T18:36:20.220 回答
3

通过设置HttpClientFactory可以使用 Polly 配置的 Flurl 并创建自定义HttpClientFactory

public class MyCustomHttpClientFactory : DefaultHttpClientFactory, IMyCustomHttpClientFactory
{
    private readonly HttpClient _httpClient;
    public MyCustomHttpClientFactory(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    public override HttpClient CreateHttpClient(HttpMessageHandler handler)
    {
        return _httpClient;
    }
}

通过以下方式注册该服务ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddHttpClient<IMyCustomHttpClientFactory, MyCustomHttpClientFactory>()
        .SetHandlerLifetime(...)
        .AddPolicyHandler(....);
}

并将该工厂分配给 Flurl:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Get HttpClientFactory and Configure Flurl to use it.
    var factory = (IMyCustomHttpClientFactory)app.ApplicationServices.GetService(typeof(IMyCustomHttpClientFactory));
    FlurlHttp.Configure((settings) => settings.HttpClientFactory = factory);
}
于 2018-09-11T11:53:35.463 回答