You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Polly重试回调中获取HttpClient的完整URI?

在Polly重试回调中获取完整请求URI的解决方案

我之前也碰到过这个问题,要在Polly的重试回调里拿到请求的完整URI,核心思路就是把请求URI传递到Polly的Context中——毕竟重试回调参数里正好有context,我们提前把URI存进去,回调时直接取出来就行。

下面分享两种可行的方法,推荐第二种,更通用省心:

方法一:手动在请求时传入Polly Context(适合少量请求场景)

如果只是个别请求需要记录重试URI,可以在调用HttpClient时手动创建并传入Polly Context,把URI存进去:

在你的JoinPackageApiClient请求方法里:

public async Task<YourResponseType> CallSomeEndpointAsync()
{
    var requestPath = "/b/c?d=1";
    // 拼接完整URI并存入Context
    var fullUri = new Uri(_client.BaseAddress, requestPath).ToString();
    var pollyContext = new Context { ["RequestUri"] = fullUri };

    // 调用HttpClient时传入Context
    var response = await _client.GetAsync(requestPath, pollyContext);
    
    // 后续处理逻辑...
}

然后在GetRetryPolicy的onRetry回调里读取:

onRetry: (response, delay, retryCount, context) =>
{
    if (context.TryGetValue("RequestUri", out var uriObj) && uriObj is string requestUri)
    {
        // 这里就能拿到完整URI,比如https://a.com/b/c?d=1
        Console.WriteLine($"正在重试第 {retryCount} 次,请求地址:{requestUri}");
    }
}

这种方法的缺点是每个需要重试的请求都要手动处理Context,不够优雅,适合请求量少的场景。

方法二:用自定义DelegatingHandler自动注入URI到Context(通用方案)

如果所有HttpClient请求都需要记录重试URI,推荐用这种方法——通过自定义消息处理程序,自动把请求的完整URI存入Polly Context,不用每个请求都手动处理。

步骤1:创建自定义DelegatingHandler

public class PollyContextUriInjector : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        // 获取请求关联的Polly Context,没有就新建一个
        var pollyContext = request.GetPolicyExecutionContext() ?? new Context();
        
        // 把完整请求URI存入Context
        if (request.RequestUri != null)
        {
            pollyContext["RequestUri"] = request.RequestUri.ToString();
        }
        
        // 把Context重新关联到请求上
        request.SetPolicyExecutionContext(pollyContext);
        
        // 继续执行后续消息处理流程
        return await base.SendAsync(request, cancellationToken);
    }
}

步骤2:注册Handler到DI容器

在Startup/Program.cs里添加:

services.AddTransient<PollyContextUriInjector>();

步骤3:给HttpClient添加这个Handler

修改你的HttpClient注册代码:

services.AddHttpClient<JoinPackageApiClient>(jp => 
{ 
    jp.BaseAddress = new Uri(appSettings.JoinPackageWS.BaseUrl); 
})
.AddHttpMessageHandler<PollyContextUriInjector>() // 添加自定义Handler
.AddPolicyHandler(GetRetryPolicy(appSettings, serviceProvider));

步骤4:在重试回调中读取URI

现在不管是响应状态码触发重试,还是超时、取消异常触发重试,都能从Context里稳定拿到完整URI:

onRetry: (response, delay, retryCount, context) =>
{
    if (context.TryGetValue("RequestUri", out var uriObj) && uriObj is string requestUri)
    {
        // 比如写入日志
        _logger.LogWarning("重试请求:{RequestUri},第{RetryCount}次,延迟{DelaySeconds}秒", 
            requestUri, retryCount, delay.TotalSeconds);
    }
}

为什么不直接用response.RequestUri?

要注意:如果重试是因为异常(比如TimeoutRejectedException、TaskCanceledException)触发的,onRetry回调里的response参数会是null,这时候就无法通过response.RequestMessage?.RequestUri获取URI了。而用Context的方法不管是异常还是响应问题,都能稳定拿到URI,所以更可靠。

内容的提问来源于stack exchange,提问作者Royi Namir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 17:17:36