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

