.NET6如何全局禁用非空值类型隐式添加[Required]特性
请参考如下简单模型定义:
public class TestClass { [MyRequired(ErrorMessage = "Some error message")] public int TestVariable { get; set; } }
上述定义会被框架隐式追加[Required]特性,最终渲染的HTML会同时包含[Required]与自定义[MyRequired]对应的data-val验证属性,造成重复验证。
目前已尝试的两种解决方案均存在问题:
- 方案1:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;,仅支持.NET 5及更早版本,.NET 6中不可用 - 方案2:
services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);,该配置仅针对非空引用类型生效,int属于值类型,因此配置不生效
临时解决方案是将字段声明为可空值类型:
public int? TestVariable { get; set; }
但不确定该写法是否符合开发最佳实践,需要找到全局禁用隐式特性追加行为的方法,无需将所有值类型都声明为可空类型。
你尝试的第二个方案只对引用类型生效,.NET Core 3.0 及以上版本(包含.NET 5/6/7/8)已经把原来DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes的配置迁移到了MvcOptions中,对应属性是SuppressImplicitRequiredAttributeForValueTypes,和你用的引用类型开关是两个独立配置,全局配置方式如下:
// .NET 6+ 顶级语句Program.cs写法 builder.Services.AddControllersWithViews(options => { // 全局关闭值类型隐式追加[Required]特性的行为 options.SuppressImplicitRequiredAttributeForValueTypes = true; });
如果项目使用Razor Pages,在AddRazorPages()的配置委托中设置该属性同样生效。配置完成后框架不会再给非空值类型自动注入内置[Required]特性,只会保留你自定义的[MyRequired]特性对应的验证逻辑,不会出现重复的data-val属性,也无需修改模型把值类型统一声明为可空类型。
关于你临时用的可空值类型方案:仅当业务逻辑上该字段确实允许缺省、传空值时,声明为int?才是符合最佳实践的写法;如果字段本身属于必填项,只是需要替换默认的Required校验规则,强行修改为可空值类型属于为绕开框架默认行为破坏模型的类型约束,不推荐使用。
内容的提问来源于stack exchange,提问作者DimChtz

