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

.NET 5环境下如何用Polly实现剔除无效DTO后重试POST请求

Polly 适配场景说明与实现方案

这个场景完全可以通过Polly实现,核心思路是结合Polly的重试策略与上下文传递能力,每次捕获400响应后动态过滤校验失败的DTO,更新请求载荷后发起重试,以下是.NET 5环境下的完整实现步骤:

1. 定义基础模型

首先定义业务DTO和接口400错误响应的结构,需和目标接口返回的错误格式对齐:

// 业务DTO示例
public class MyBusinessDto
{
    // 对应响应中返回的校验失败ID字段
    public string Id { get; set; }
    // 其他业务属性...
}

// 目标接口400响应的结构,可根据实际返回格式调整
public class BatchValidationErrorResp
{
    // 所有校验失败的DTO ID列表
    public List<string> FailedDtoIds { get; set; }
}

2. 注册Polly重试策略与类型化HttpClient

在Startup.cs的ConfigureServices方法中注册策略和HttpClient:

public void ConfigureServices(IServiceCollection services)
{
    // 自定义批量校验重试策略
    var batchValidateRetryPolicy = Policy
        // 捕获返回400状态码的响应
        .HandleResult<HttpResponseMessage>(resp => resp.StatusCode == System.Net.HttpStatusCode.BadRequest)
        // 设置最大重试次数,可根据业务调整
        .RetryAsync(retryCount: 5, onRetryAsync: async (result, _, context) =>
        {
            // 解析响应中的校验失败ID列表
            var errorResp = await result.Result.Content.ReadFromJsonAsync<BatchValidationErrorResp>();
            // 未拿到失败ID列表时终止重试
            if (errorResp?.FailedDtoIds == null || !errorResp.FailedDtoIds.Any())
            {
                context["StopRetry"] = true;
                return;
            }

            // 从上下文取出当前请求的所有DTO,过滤掉校验失败的条目
            var currentDtos = context["RequestPayload"] as List<MyBusinessDto>;
            var validDtos = currentDtos?.Where(dto => !errorResp.FailedDtoIds.Contains(dto.Id)).ToList();
            
            // 所有DTO都校验失败时终止重试
            if (validDtos == null || !validDtos.Any())
            {
                context["StopRetry"] = true;
                return;
            }

            // 更新上下文中的DTO列表,下次重试使用过滤后的结果
            context["RequestPayload"] = validDtos;
        });

    // 注册类型化HttpClient,绑定重试策略
    services.AddHttpClient<IBatchApiClient, BatchApiClient>(client =>
    {
        client.BaseAddress = new Uri("https://你的目标接口域名/");
        // 其他HttpClient全局配置...
    })
    .AddPolicyHandler((_, request) =>
    {
        // 仅对标记了批量校验重试的请求应用策略,不影响该客户端的其他接口请求
        if (request.Properties.TryGetValue("EnableBatchValidateRetry", out _))
        {
            // 包装一层终止逻辑,避免不需要重试的场景继续执行
            return batchValidateRetryPolicy.WrapAsync(Policy.HandleResult<HttpResponseMessage>(resp =>
            {
                var context = request.GetPolicyExecutionContext();
                if (context.TryGetValue("StopRetry", out var stopFlag) && (bool)stopFlag)
                {
                    return false;
                }
                return resp.StatusCode == System.Net.HttpStatusCode.BadRequest;
            }));
        }
        // 其他请求不应用策略
        return Policy.NoOpAsync<HttpResponseMessage>();
    });
}

3. 实现类型化HttpClient的请求方法

// 类型化客户端接口
public interface IBatchApiClient
{
    Task<HttpResponseMessage> PostBatchDtosAsync(List<MyBusinessDto> dtos, CancellationToken cancellationToken = default);
}

// 类型化客户端实现
public class BatchApiClient : IBatchApiClient
{
    private readonly HttpClient _httpClient;

    public BatchApiClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<HttpResponseMessage> PostBatchDtosAsync(List<MyBusinessDto> dtos, CancellationToken cancellationToken = default)
    {
        // 初始化Polly上下文,存入初始请求载荷
        var pollyContext = new Context();
        pollyContext["RequestPayload"] = dtos;

        using var requestMsg = new HttpRequestMessage(HttpMethod.Post, "目标接口路径")
        {
            // 给请求打标记,触发上面注册的重试策略
            Properties = { {"EnableBatchValidateRetry", true} }
        };
        // 绑定上下文到请求
        requestMsg.SetPolicyExecutionContext(pollyContext);

        while (true)
        {
            // 每次发请求前从上下文取最新的过滤后DTO列表序列化
            var currentPayload = pollyContext["RequestPayload"] as List<MyBusinessDto>;
            requestMsg.Content = JsonContent.Create(currentPayload);

            var response = await _httpClient.SendAsync(requestMsg, cancellationToken);
            
            // 非400响应或上下文标记终止重试时,直接返回结果
            if (response.StatusCode != System.Net.HttpStatusCode.BadRequest 
                || (pollyContext.TryGetValue("StopRetry", out var stopFlag) && (bool)stopFlag))
            {
                return response;
            }
        }
    }
}

4. 业务层调用示例

直接注入IBatchApiClient调用对应方法即可,不需要额外处理重试逻辑:

public class MyBusinessService
{
    private readonly IBatchApiClient _batchApiClient;

    public MyBusinessService(IBatchApiClient batchApiClient)
    {
        _batchApiClient = batchApiClient;
    }

    public async Task DoBatchOperationAsync(List<MyBusinessDto> dtos)
    {
        var resp = await _batchApiClient.PostBatchDtosAsync(dtos);
        resp.EnsureSuccessStatusCode();
        // 后续业务处理...
    }
}

注意事项

  • 最大重试次数可根据实际业务场景调整,建议设置上限避免极端情况下无限重试
  • 如果目标接口的400响应是标准的ProblemDetails格式,可直接将解析类型替换为ProblemDetails,对应取扩展字段中的失败ID列表即可
  • 如需留存校验失败的DTO信息,可在策略的重试回调中把FailedDtoIds存入日志或上下文返回给业务层

内容的提问来源于stack exchange,提问作者ravi kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 21:45:01