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

FluentValidation校验列表时如何返回去重的唯一错误信息

FluentValidation 集合校验相同错误去重方案

方案1:校验结果统一去重(侵入性最低,推荐)

直接在校验执行完成后,对返回的错误集合按唯一错误标识去重,不需要修改原有校验规则,适合大部分场景。
首先编写通用扩展方法:

public static class ValidationResultExtensions
{
    /// <summary>
    /// 按错误唯一标识去重,相同类型错误仅保留第一条
    /// </summary>
    public static ValidationResult DeduplicateErrors(this ValidationResult result)
    {
        if (result.IsValid) return result;
        
        var uniqueFailures = result.Errors
            .GroupBy(f => 
                f.CustomState is Error err 
                    ? $"{err.Code}:{err.Message}" // 替换为你Error类中可唯一标识错误类型的字段组合
                    : f.ErrorMessage
            )
            .Select(g => g.First())
            .ToList();
        
        result.Errors.Clear();
        result.Errors.AddRange(uniqueFailures);
        return result;
    }
}

调用时在Validate后追加去重逻辑即可:

var validateResult = validator.Validate(yourModel).DeduplicateErrors();

如果是ASP.NET Core自动校验管道,可以在验证过滤器/结果过滤器中统一对ValidationResult做去重处理,不需要修改业务侧校验代码。

方案2:集合层级聚合校验(适合不需要定位错误元素的场景)

放弃逐元素的ForEach校验,直接在集合维度写校验规则,只要集合中存在任意不符合规则的元素,就只返回一次对应错误。
示例代码替换原有集合校验规则:

RuleFor(x => x.Items)
    .NotEmpty()
    .WithState(x => new Error("ItemsEmpty", "集合不能为空"))
    .Must(items => items.All(i => !string.IsNullOrWhiteSpace(i.Name)))
    .WithState(_ => new Error("ItemNameEmpty", "存在元素名称为空"))
    .Must(items => items.All(i => !string.IsNullOrWhiteSpace(i.Category)))
    .WithState(_ => new Error("ItemCategoryEmpty", "存在元素分类为空"));

这种方案的局限是无法标记具体哪个下标的元素出错,仅适合只需要校验规则是否通过、不需要错误定位的场景。

方案3:自定义去重版ForEach逻辑

如果需要保留逐元素校验的能力,同时在执行过程中自动去重同类型错误,可以封装自定义的集合校验扩展,内部维护已触发的错误类型集合,同类型错误仅添加一次。
核心实现代码:

public static class RuleBuilderExt
{
    public static IRuleBuilderOptions<T, IEnumerable<TItem>> UniqueErrorForEach<T, TItem>(
        this IRuleBuilder<T, IEnumerable<TItem>> ruleBuilder,
        IValidator<TItem> itemValidator)
    {
        return ruleBuilder.Must((root, items, context) =>
        {
            var triggeredErrorSet = new HashSet<string>();
            var isValid = true;
            foreach (var item in items)
            {
                var itemRes = itemValidator.Validate(item);
                if (itemRes.IsValid) continue;
                
                foreach (var failure in itemRes.Errors)
                {
                    var errorKey = failure.CustomState is Error err 
                        ? $"{err.Code}:{err.Message}" 
                        : failure.ErrorMessage;
                    
                    // 同类型错误第一次出现才加入结果
                    if (triggeredErrorSet.Add(errorKey))
                    {
                        context.AddFailure(failure);
                        isValid = false;
                    }
                }
            }
            return isValid;
        });
    }
}

替换原有ForEach逻辑即可使用:

RuleFor(x => x.Items)
    .NotEmpty()
    .WithState(x => new Error("ItemsEmpty", "集合不能为空"))
    .UniqueErrorForEach(new SingleItemValidator());

注意:去重时使用的Key必须是能唯一标识错误类型的固定值,不要把元素下标、动态生成的字段值拼入Key,否则会导致去重失效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:15:45