c# - 使用 Polly 重试之前检查响应的字符串内容

标签 c# polly retry-logic

我正在使用一个非常不稳定的 API。有时我会收到 500 服务器错误Timeout,有时我也会收到 500 服务器错误,因为我给它提供了它无法处理的输入

SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.

这两种情况都会给我 HttpRequestException 但我可以查看来自服务器的回复消息并确定异常的原因。如果是超时错误,我应该再试一次。如果输入错误,我应该重新抛出异常,因为再多的重试也无法解决错误数据的问题。

我想对 Polly 做的是在尝试重试之前检查响应消息。但到目前为止我看到的所有示例都只包含异常类型。

到目前为止我已经想出了这个:

        HttpResponseMessage response = null;
        String stringContent = null;
        Policy.Handle<FlakyApiException>()
             .WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
              async (exception, timeSpan, context) =>
            {
                response = await client.PostAsync(requestUri, new StringContent(serialisedParameters, Encoding.UTF8, "application/json"));
                stringContent = await response.Content.ReadAsStringAsync();

                if (response.StatusCode == HttpStatusCode.InternalServerError && stringContent.Contains("Timeout"))
                {
                    throw new FlakyApiException(stringContent);
                }
            });

是否有更好的方法来进行此类检查?

最佳答案

一般来说,您可以配置 Polly 策略来响应执行结果(而不仅仅是异常),例如使用谓词检查 HttpResponseMessage.StatusCode。示例 here in the Polly readme .

但是,没有一种内置方法来配置单个 Polly 策略来额外响应响应消息的内容。这是因为(如您的示例所示)获取该内容需要第二次异步调用,这本身可能会引发网络错误。

这个tl;dr使如何(用简单的语法)表达单个策略变得复杂,该策略管理两个不同的异步步骤,每个步骤可能有不同的错误处理。先前相关讨论on Polly github : 欢迎评论。

因此,当一个序列需要两个单独的异步调用时,Polly 团队目前建议将其表达为两个单独的策略,类似于 the example in the end of this answer .

<小时/>

您问题中的特定示例可能不起作用,因为 onRetryAsync 委托(delegate)(抛出 FlakyApiException)本身不受策略保护。策略仅保护通过 .Execute/ExecuteAsync(...) 执行的委托(delegate)的执行。

<小时/>

一种方法可能是使用两种策略,一种是重试策略,用于重试所有典型的 http 异常和状态代码(包括 500);然后里面有一个 Polly FallbackPolicy它捕获表示 SqlDateTime 溢出 的状态代码 500,并通过重新抛出某些区别异常 (CustomSqlDateOverflowException) 来排除重试。

        IAsyncPolicy<HttpResponseMessage> rejectSqlError = Policy<HttpResponseMessage>
            .HandleResult(r => r.StatusCode == HttpStatusCode.InternalServerError)
            .FallbackAsync(async (delegateOutcome, context, token) =>
            {
                String stringContent = await delegateOutcome.Result.Content.ReadAsStringAsync(); // Could wrap this line in an additional policy as desired.
                if (delegateOutcome.Result.StatusCode == HttpStatusCode.InternalServerError && stringContent.Contains("SqlDateTime overflow"))
                {
                    throw new CustomSqlDateOverflowException(); // Replace 500 SqlDateTime overflow with something else.
                }
                else
                {
                    return delegateOutcome.Result; // render all other 500s as they were
                }
            }, async (delegateOutcome, context) => { /* log (if desired) that InternalServerError was checked for what kind */ });

        IAsyncPolicy<HttpResponseMessage> retryPolicy = Policy<HttpResponseMessage>
            .Handle<HttpRequestException>()
            .OrResult(r => r.StatusCode == HttpStatusCode.InternalServerError)
            .OrResult(r => /* condition for any other errors you want to handle */)
            .WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                async (exception, timeSpan, context) =>
                {
                    /* log (if desired) retry being invoked */
                });

        HttpResponseMessage response = await retryPolicy.WrapAsync(rejectSqlError)
            .ExecuteAsync(() => client.PostAsync(requestUri, new StringContent(serialisedParameters, Encoding.UTF8, "application/json"), cancellationToken));

关于c# - 使用 Polly 重试之前检查响应的字符串内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50835992/

相关文章:

c# - 格式化为两位小数

c# - 在 C# 中将调试字符串排除在构建之外

c# - Polly WaitAndRetryAsync 在重试一次后挂起

Azure 数据工厂 - 重试插入复制事件 (Azure SQL DB)

c# - 如何重试与 ASP.NET Core、EF Core 的乐观并发冲突?

c# - Polly HandleTransientHttpError 没有捕捉到 HttpRequestException

c# - 如何使此类通用? (.NET C#)

c# - jquery 自动完成不选取动态数据

c# - 如何仅为设置的特定 StatusCodes 设置 Polly Retry

c# - 记录异常并重新抛出的 Polly 策略