Fluent Validation校验对象列表:留存有效数据并返回明细结果
问题描述
我有如下请求模型,并用Fluent Validation做了校验:
public class Request { public List<Customer> Customers { get; set; } } public class Customer { public int CustNumber { get; set; } //必填字段 public string CustName { get; set; } //必填字段 public string CustAddress { get; set; } //必填字段 }
当前的Fluent Validation实现会直接返回400 BadRequest拒绝整个请求:
When(model => model.Customers!= null, () => { RuleForEach(model => model.Customers).SetValidator(new CustomerValidator()); }); public CustomerValidator() { RuleFor(item => item.CustNumber).NotEmpty(); RuleFor(item => item.CustName).NotEmpty(); RuleFor(item => item.CustAddress).NotEmpty(); }
我需要实现:当Customers列表里有部分对象不符合校验规则时(比如3个对象里1个没传必填的CustNumber),把剩余有效对象保存到数据库,同时返回200 HTTP响应,在响应里列出所有项的处理结果——有效项显示成功,无效项显示对应的错误信息。
请求示例
{ "Customers": [ { "CustNumber": 123, "CustName": "Test1", "CustAddress": "US" }, { "CustNumber": 456, "CustName": "Test2", "CustAddress": "US" }, { "CustName": "Test3", "CustAddress": "US" } ] }
期望响应
{ "CustResult": [ { "CustNumber": 123, "Status": "Success" }, { "CustNumber": 456, "Status": "Success" }, { "Status": "Customers[2].CustNumber is required" } ] }
实现方案
要实现这个需求,不能依赖Fluent Validation的全局自动校验(它默认会在发现错误时直接返回400),需要手动拆分校验和业务处理流程:
1. 手动校验每个客户,收集结果并分离有效数据
先定义结果模型,然后在业务逻辑中逐个校验Customer对象,区分有效/无效项,同时收集处理结果:
// 定义响应结果模型 public class CustomerResult { public int? CustNumber { get; set; } public string Status { get; set; } } // 业务处理方法 public IActionResult BatchProcess(Request request) { var custResults = new List<CustomerResult>(); var validCustomers = new List<Customer>(); if (request.Customers != null) { var customerValidator = new CustomerValidator(); for (int index = 0; index < request.Customers.Count; index++) { var currentCust = request.Customers[index]; var validationResult = customerValidator.Validate(currentCust); if (validationResult.IsValid) { // 有效数据加入待保存列表 validCustomers.Add(currentCust); custResults.Add(new CustomerResult { CustNumber = currentCust.CustNumber, Status = "Success" }); } else { // 拼接错误信息,带上列表索引 var errorMsg = validationResult.Errors.Select(e => $"Customers[{index}].{e.PropertyName} is required") .FirstOrDefault(); custResults.Add(new CustomerResult { CustNumber = currentCust.CustNumber != 0 ? currentCust.CustNumber : null, Status = errorMsg ?? "Invalid customer data" }); } } } // 批量保存有效客户到数据库 if (validCustomers.Any()) { _customerDbSet.AddRange(validCustomers); _dbContext.SaveChanges(); } // 返回200响应和完整处理结果 return Ok(new { CustResult = custResults }); }
2. 禁用全局自动校验(若已启用)
如果项目中配置了Fluent Validation的全局自动校验(比如AddFluentValidation()),需要针对该接口跳过全局校验,避免提前返回400。在ASP.NET Core中可以通过特性实现:
[ApiController] [Route("api/customers")] public class CustomerController : ControllerBase { [HttpPost("batch-add")] [SkipValidation] // 使用FluentValidation.AspNetCore提供的SkipValidation特性 public IActionResult BatchAdd(Request request) { // 执行上述业务处理逻辑 } }
3. 自定义错误信息格式(可选)
如果需要更贴合示例的错误信息格式,可以直接指定错误文本,或者根据校验规则的错误信息拼接:
// 替换错误信息生成逻辑 var firstError = validationResult.Errors.First(); var errorMsg = $"Customers[{index}].{firstError.PropertyName} {firstError.ErrorMessage}";
通过以上步骤,即可实现有效数据入库、返回200响应并包含所有项处理结果的需求。
内容的提问来源于stack exchange,提问作者Dark Knight
相关产品推荐
相关产品推荐

