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

How can I use Polly to retry x number of times based on response content and then return the response?

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

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

这可以做到吗?

编辑:

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

这有几个部分。

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

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

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

...

var httpResponse = ...
var responseContent = ...
if (InternalServerResponse(responseContent))
{
    httpResponse.RequestMessage.Properties[IsInternalServerResponseKey] = true;
}

这样就有一个标志附加到 request/response 可以被代码的其他部分读取,特别是 Polly 重试处理程序。

解决方案的另一部分是重试次数。通常,Polly 有委托来确定它是否应该重试,并且这些委托是独立的 - 重试这种类型的异常,或者重试类似的响应。在这种情况下,您想要重试匹配特定形状 but 的响应,前提是重试次数不多,并且重试次数过多且响应匹配“重试”形状,那么你不想抛出异常,而是return响应。

这很不寻常,但可行。您需要在 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);
}

我还没有测试过这个;传递给 WaitAndRetryAsync2 和用于比较 retryCount.

2 之间可能存在差一错误