【问题标题】:Reusing HttpRequestMessage in Polly retry policies在 Polly 重试策略中重用 HttpRequestMessage
【发布时间】:2018-05-24 20:00:07
【问题描述】:

HttpRequestMessage 对象只能使用一次;以后尝试使用同一个对象会引发异常。我正在使用 Polly 重试一些请求,我遇到了这个问题。我知道如何克隆请求,有很多关于 SO 的示例,但我不知道如何克隆请求并在 Polly 重试时发送新请求。我怎样才能做到这一点?

这些是我的政策,供参考。这是一个 Xamarin 应用程序。如果出现网络故障,我想重试几次,如果响应未经授权,我想使用保存的凭据重新验证并再次尝试原始请求。

public static PolicyWrap<HttpResponseMessage> RetryPolicy
{
    get => WaitAndRetryPolicy.WrapAsync(ReAuthPolicy);
}

private static IAsyncPolicy WaitAndRetryPolicy
{
    get => Policy.Handle<WebException>().WaitAndRetryAsync(4, _ => TimeSpan.FromSeconds(2));
}

private static IAsyncPolicy<HttpResponseMessage> ReAuthPolicy
{
    get => Policy.HandleResult<HttpResponseMessage>(x => x.StatusCode == HttpStatusCode.Unauthorized)
        .RetryAsync((_, __) => CoreService.LogInWithSavedCredsAsync(true));
}

由于 HttpRequestMessage 重用,这不起作用,但这是我想要完成的:

var request = new HttpRequestMessage(HttpMethod.Post, "some_endpoint")
{
    Content = new StringContent("some content")
};

request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

var policyResponse = await ConnectivityHelper.RetryPolicy
    .ExecuteAndCaptureAsync(() => _client.SendAsync(request)).ConfigureAwait(false);

// handle outcome

【问题讨论】:

  • this q,尤其是this answer。将重试放在 DelegatingHandler 中可以避免无法重用的问题。如果 ASPNET Core 和你可以等到 2.1 RTM,请考虑新的IHttpClientFactory。如果是这样,请参阅Polly's doco
  • @mountaintraveller 谢谢,不知何故我没见过那个。我不确定如何将它与我已经拥有的政策结合起来,但我无法理解 Polly 政策应该如何协同工作。 (这是一个 Xamarin 应用程序,但我认为这个问题足够笼统,所以我没有包括在内。)
  • Polly doco on IHttpClientFactory 讨论了when multiple policies are in use。这涵盖了在没有 IHttpClientFactory 的情况下使用 DelegatingHandler 方法应用多个策略的情况。有关该概念的讨论,请参阅Steve Gordon's blog。如果在 ASPNET Core 2.1 之前,您必须手动创建 DelegatingHandlers 链。
  • 如果您的查询一般是关于组合 Polly 策略时的行为,请参阅Polly wiki on PolicyWrap
  • 是的,DelegatingHandler 是 HttpClient 的中间件。没有办法将 ExecuteandCaptureAsync() 与它结合起来——也就是说,在 DelegatingHandler 内部。 ExecuteandCaptureAsync() 改变执行的返回类型;但中间件做不到。

标签: c# dotnet-httpclient polly


【解决方案1】:

如果 HttpRequestMessage 被重用,则抛出 InvalidOperationException 的代码是 HttpClient 本身内的验证步骤。

    private static void CheckRequestMessage(HttpRequestMessage request)
    {
        if (!request.MarkAsSent())
        {
            throw new InvalidOperationException(SR.net_http_client_request_already_sent);
        }
    }

https://github.com/microsoft/referencesource/blob/e7b9db69533dca155a33f96523875e9c50445f44/System/net/System/Net/Http/HttpClient.cs#L628-L634

    internal bool MarkAsSent()
    {
        return Interlocked.Exchange(ref sendStatus, messageAlreadySent) == messageNotYetSent;
    }

https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System/net/System/Net/Http/HttpRequestMessage.cs#L193

您可以将 polly 重试策略放在 DelegatingHandler 中,这样就可以了。它还提供了一个不错的 SoC(关注点分离)。如果将来您希望重试或更改重试行为,只需删除DelegatingHandler 或更改它。注意处理掉HttpRequestMessage 和中间HttpResponseMessages 对象。

【讨论】:

    猜你喜欢
    • 2022-09-23
    • 2022-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多