.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
相关产品推荐
相关产品推荐

