如何使用 Polly 根据响应内容重试 x 次,然后返回响应?

Vin*_*mth 5 c# .net-core polly

在我的应用程序中,我使用Polly库来调用 API。

API 可以在响应中返回警告和错误。对于其中一些警告,我想重试 2 次,下次我想将响应返回给调用者

这可以做到吗?

编辑:

@StephenCleary 指出我应该只处理响应而不是抛出异常。

要检查响应,我需要等待内容。以下内容无法编译,有人能看到我该怎么做吗?

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(async msg =>
            {
               var content  = await msg.Content.ReadAsStringAsync();
               return content.Contains("errorcode123");
            })
            .WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 4

这有几个部分。

首先,如果结果有警告,您不想抛出异常。那时您可能想重试,也可能不想;那里的代码还不能告诉。但是抛出异常意味着响应被丢弃,因此此时抛出异常是不正确的。

相反,该处理程序应该使用“有警告”标志来标记响应。这可以使用HttpRequestMessage.PropertiesHttpRequestMessage.Options在 .NET 5 中)。像这样的东西:

private static readonly string IsInternalServerResponseKey = Guid.NewGuid().ToString("N");

...

var httpResponse = ...
var responseContent = ...
if (InternalServerResponse(responseContent))
{
    httpResponse.RequestMessage.Properties[IsInternalServerResponseKey] = true;
}
Run Code Online (Sandbox Code Playgroud)

这样,请求/响应就会附加一个标志,代码的其他部分(特别是 Polly 重试处理程序)可以读取该标志。

解决方案的另一部分是重试计数。通常,Polly 具有确定是否应该重试的委托,并且这些委托是独立的 - 重试这种类型的异常,或者重试类似的响应。在这种情况下,您想要重试与特定形状匹配的响应,前提是重试次数不多,并且如果重试次数太多并且响应与“重试”形状匹配,那么您希望抛出异常但返回响应。

这是不寻常的,但是可行的。您需要捕获 Polly 上下文中的“外部考虑因素”(在本例中为重试计数)。然后,您的重试委托可以从上下文中提取重试计数,并据此做出决定。像这样的东西应该有效:

private static readonly string RetryCountKey = Guid.NewGuid().ToString("N");
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(response =>
        {
            return IsInternalServerResponse() && RetryCount() <= 2;

            bool IsInternalServerResponse()
            {
                if (!response.RequestMessage.Properties.TryGetValue(IsInternalServerResponseKey, out var objValue) ||
                    objValue is not bool boolValue)
                    return false;
                return boolValue;
            }

            int RetryCount()
            {
                if (!response.RequestMessage.GetPolicyExecutionContext().TryGetValue(RetryCountKey, out var objValue) ||
                    objValue is not int intValue)
                    return 0;
                return intValue;
            }
        })
        .WaitAndRetryAsync(2,
            (retryAttempt, _) => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
            (_, _, retryAttempt, context) => context[RetryCountKey] = retryAttempt);
}
Run Code Online (Sandbox Code Playgroud)

我还没有测试过这个;2传递到的WaitAndRetryAsync2用于比较 的之间可能存在相差一的错误retryCount