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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 06:35:29