ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FluentValidation 错误消息与属性名定制完全指南:WithMessage / WithName / OverrideIndexer 深度解析

FluentValidation 错误消息与属性名定制完全指南:WithMessage / WithName / OverrideIndexer 深度解析 后端【免费下载链接】FluentValidationA popular .NET validation library for building strongly-typed validation rules.项目地址https://gitcode.com/gh_mirrors/fl/FluentValidation点击查看免费下载导读本指南基于 FluentValidation 官方文档 docs/configuring.md系统讲解如何定制验证失败时生成的错误消息与属性名从最常用的WithMessage覆盖默认消息、占位符Placeholder机制到WithName/OverridePropertyName的差别再到集合校验时OverrideIndexer控制索引显示格式。读完本文你将能够为每个验证规则产出面向用户的可读错误提示并理解这些 API 在源码层面DefaultValidatorOptions.cs、MessageFormatter.cs、PropertyChain.cs是如何被实现和串联的。一、用 WithMessage 覆盖默认错误消息FluentValidation 的每个内置校验器都有默认的错误文案多数来自本地化资源详见 localization。当默认文案不符合业务需要时可以在规则链上调用WithMessage指定自定义消息RuleFor(customer customer.Surname).NotNull().WithMessage(Please ensure that you have entered your Surname);在源码层面WithMessage字符串重载定义于 DefaultValidatorOptions.cs它通过Configurable(rule).Current.SetErrorMessage(errorMessage)将自定义消息挂到紧随其后的那条规则组件上因此它只作用于链上直接相邻的校验器。例如NotNull().NotEmpty()之后调用WithMessage只会影响NotEmpty那一条。消息占位符机制自定义消息中可以嵌入{PropertyName}这类占位符运行时会被替换为实际值RuleFor(customer customer.Surname).NotNull().WithMessage(Please ensure you have entered your {PropertyName}); // 失败时输出Please ensure you have entered your Surname占位符的替换由 MessageFormatter.cs 完成BuildMessage内部用正则{([^{}:])(?::([^{}]))?}扫描消息模板并从PlaceholderValues字典中取值替换。值得一提的细节是正则中的第二个分组支持格式说明符即占位符可以写成{PropertyValue:0.00}这类带格式化字符串的形式最终通过string.Format({0:格式}, value)完成格式化见 MessageFormatter.cs。各类校验器支持的占位符所有校验器通用{PropertyName}—— 被验证属性的名称{PropertyValue}—— 被验证属性的值这两者同样适用于谓词校验器Must、邮箱校验器和正则校验器。比较类校验器Equal、NotEqual、GreaterThan、GreaterThanOrEqual、LessThan、LessThanOrEqual、ExclusiveBetween、InclusiveBetween等额外支持{ComparisonValue}—— 用于比较的值{ComparisonProperty}—— 被比较的属性名如果存在Length 校验器专用{MinLength}—— 最小长度{MaxLength}—— 最大长度{TotalLength}—— 用户实际输入的长度每个内置校验器支持哪些占位符的完整清单可查阅 built-in-validators 中对应校验器的说明。用 lambda 动态构造消息WithMessage还提供两个 lambda 重载DefaultValidatorOptions.cs可以引用被校验对象上的其他属性或常量值// 在消息中引用常量 RuleFor(customer customer.Surname) .NotNull() .WithMessage(customer string.Format(This message references some constant values: {0} {1}, hello, 5)) // 结果This message references some constant values: hello 5 // 引用对象上的其他属性字符串插值 RuleFor(customer customer.Surname) .NotNull() .WithMessage(customer $This message references some other properties: Forename: {customer.Forename} Discount: {customer.Discount}) // 结果This message references some other properties: Forename: Jeremy Discount: 100lambda 重载在实现上通过SetErrorMessage((ctx, val) ...)注册一个延迟执行的委托DefaultValidatorOptions.cs这意味着消息在验证失败时才求值而非定义规则时求值。注意WithMessage只会覆盖某一条规则的文案。如果你希望全局替换所有默认消息例如统一走公司内部的多语言资源应使用 FluentValidation 的本地化能力参考 localization。二、用 WithName 定制错误消息中的属性名默认错误消息会把被验证的属性名嵌入其中。例如RuleFor(customer customer.Surname).NotNull();失败时默认消息形如Surname must not be empty.。若只想替换消息里的属性名可调用WithNameRuleFor(customer customer.Surname).NotNull().WithName(Last name); // 失败时输出Last name must not be empty.测试用例 AbstractValidatorTester.cs 验证了这一点WithName(First Name)后Forename的 NotNull 失败消息为First Name must not be empty.WithName同样有 lambda 重载DefaultValidatorOptions.cs可动态计算显示名RuleFor(customer customer.Surname).NotNull().WithName(customer Last name for customer customer.Id);测试 AbstractValidatorTester.cs 展示了用WithName(x x.Surname)把显示名取自另一个属性的用法。WithName 与 OverridePropertyName 的区别WithName只影响消息中的显示名。当检查ValidationResult.Errors时该失败项仍关联到原始属性Surname——也就是说PropertyName、PropertyName路径、校验选择器validator selector等逻辑都照旧工作。OverridePropertyName则是完全重命名属性DefaultValidatorOptions.cs它直接改写规则底层记录的PropertyNameRuleFor(customer customer.Surname).NotNull().OverridePropertyName(foo);从源码看OverridePropertyName通过Configurable(rule).PropertyName propertyName设置规则属性名DefaultValidatorOptions.cs因此ValidationResult.Errors[0].PropertyName会变成foo见测试 AbstractValidatorTester.cs。它还有一个接收 lambda 表达式的重载会自动提取表达式中成员名RuleFor(customer customer.Surname).NotNull().OverridePropertyName(x x.Forename); // Errors[0].PropertyName Forename源码注释明确提醒OverridePropertyName属于高级特性大多数场景下你真正想要的是WithNameDefaultValidatorOptions.cs。另有使用场景当RuleFor无法从表达式推断属性名时例如对IEnumerable调用RuleForEach需要显式调用OverridePropertyName否则运行时会抛出 Could not infer property name... 异常该异常消息可见于测试 ForEachRuleTests.cs。全局可插拔的显示名解析器属性显示名的解析逻辑本身是可替换的。默认情况下FluentValidation 从传入RuleFor的MemberExpression中提取成员名其默认实现是ValidatorConfiguration.DefaultDisplayNameResolver返回 null从而回退到成员名见 ValidatorOptions.cs 与 DefaultValidatorExtensions.cs。你可以通过ValidatorOptions.Global.DisplayNameResolver全局修改这一行为ValidatorOptions.Global.DisplayNameResolver (type, member, expression) { if (member ! null) { return member.Name Foo; } return null; };上面的例子会把所有属性名都加上后缀Foo这只是示意不是真实业务场景但它说明了属性显示名解析的扩展点。DisplayNameResolver属性定义于 ValidatorOptions.cs签名是FuncType, MemberInfo, LambdaExpression, string依次接收容器类型、成员信息与 lambda 表达式。当显示名未显式设置时最终显示名由 PropertyRule.cs 中的GetDisplayName决定优先使用_displayNameFactory即DisplayNameResolver的结果其次使用_displayNameWithName设置的值最后回退到按 PascalCase 拆分的属性名。三、用 OverrideIndexer 定制集合校验的索引格式用RuleForEach校验集合时失败项的属性路径会包含方括号形式的索引例如Foo.BarList[5].Baz。若希望调整这种格式比如去掉方括号、改用元素的自有属性名可以使用OverrideIndexerRuleForEach(x x.BarList) .OverrideIndexer((foo, barList, bar, i) bar.Name)回调签名是FuncT, IEnumerableTCollectionElement, TCollectionElement, int, string四个参数依次为被校验对象、整个集合、当前元素、当前索引。返回值将作为属性链上的索引器文本。上面的例子去掉方括号直接用元素属性名充当索引。源码视角索引器如何进入属性链在 CollectionPropertyRule.cs 的集合遍历中默认行为是indexer index.ToString()且useDefaultIndexFormat true当设置了IndexBuilder即OverrideIndexer注册的回调见 DefaultValidatorOptions.cs时则调用回调并关闭默认格式。随后context.PropertyChain.AddIndexer(indexer, useDefaultIndexFormat)将其拼接到属性链尾部。PropertyChain.cs 的AddIndexer决定拼接格式默认surroundWithBrackets true会生成属性[索引]例如NickNames[0]传false则直接拼接索引文本不带方括号。整个链最终由ToString用ValidatorOptions.Global.PropertyChainSeparator默认.见 ValidatorOptions.cs连接成完整路径。测试 ForEachRuleTests.cs 验证了这一点对NickNames集合{null, foo, null}调用RuleForEach(x x.NickNames) .OverrideIndexer((x, collection, element, index) index ) .NotNull()后失败项的PropertyName分别为NickNames0与NickNames2索引格式完全由回调决定。该测试还有一个异步版本MustAsync说明OverrideIndexer在同步与异步校验路径中行为一致。四、常用定制组合示例以下是一个综合示例将消息覆盖、属性名覆盖与索引覆盖结合使用public class CustomerValidator : AbstractValidatorCustomer { public CustomerValidator() { // 1. 静态消息 占位符 RuleFor(c c.Surname).NotNull().WithMessage(Please ensure you have entered your {PropertyName}); // 2. 引用其他属性的动态消息 RuleFor(c c.Surname).NotNull() .WithMessage(c $Forename is {c.Forename}, please fix the surname); // 3. 仅覆盖消息中的显示名PropertyName 仍是 Surname RuleFor(c c.Surname).NotNull().WithName(Last name); // 4. 完全重命名属性PropertyName 变为 LastName RuleFor(c c.Surname).NotNull().OverridePropertyName(LastName); // 5. 集合校验的索引格式定制 RuleForEach(c c.Orders) .OverrideIndexer((c, orders, order, i) order.OrderNumber) .SetValidator(new OrderValidator()); } }总结WithMessage覆盖单条规则的错误消息支持{PropertyName}、{PropertyValue}、比较类的{ComparisonValue}/{ComparisonProperty}以及 Length 类的{MinLength}/{MaxLength}/{TotalLength}等占位符也可用 lambda 动态求值WithName只替换消息中的显示名不影响ValidationResult中的属性关联OverridePropertyName才真正改写属性名属于高级用法OverrideIndexer通过回调重写集合校验的属性链索引文本其拼接逻辑位于 PropertyChain.AddIndexer属性显示名的全局解析逻辑可通过ValidatorOptions.Global.DisplayNameResolver扩展如需全局替换所有默认消息请参考 localization 而非逐条WithMessage。以上能力对应的完整 API 定义集中在 DefaultValidatorOptions.cs消息格式化核心在 MessageFormatter.cs相关行为均有测试覆盖如 AbstractValidatorTester.cs、ForEachRuleTests.cs可据此进一步深入阅读。赞分享后端【免费下载链接】FluentValidationA popular .NET validation library for building strongly-typed validation rules.项目地址https://gitcode.com/gh_mirrors/fl/FluentValidation点击查看免费下载相关推荐FluentValidation 本地化完全指南默认多语言消息、WithMessage 自定义与 LanguageManager 深度解析FluentValidation 本地化完全指南默认多语言消息、WithMessage 自定义与 LanguageManager 深度解析 导读 本文围绕 F后端FluentValidation自定义错误消息实战WithMessage占位符与本地化国际化完整指南FluentValidation自定义错误消息实战WithMessage占位符与本地化国际化完整指南 FluentValidation 是一款主流的 .NET后端FluentValidation 自定义错误码ErrorCode完全指南从 WithErrorCode 到消息解析原理FluentValidation 自定义错误码ErrorCode完全指南从 WithErrorCode 到消息解析原理 本文面向使用 FluentVali后端上一篇ClickPaste终极指南如何绕过Windows粘贴限制轻松实现跨应用文本输入下一篇3步快速配置Windows安装程序本地化完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表