ASP.NET Core 8 MVC模型验证疑惑:[Required]与可空类型的行为差异
核心背景:非可空引用类型(NRT)的自动验证
ASP.NET Core 3.0及以上版本,当项目启用非可空引用类型(项目文件中<Nullable>enable</Nullable>)时,框架会自动对非可空引用类型的属性进行null检查,无需额外添加[Required]特性——这是你遇到第二点现象的根本原因。
针对你的测试点逐一解释
可空类属性(
TestCModel?)
可空引用类型语法(?)明确告知框架该属性允许为null,因此未加[Required]时,未赋值(属性为null)会通过验证;添加[Required]后,框架强制该属性不能为null,未赋值自然验证失败,赋值后符合要求则通过。非可空类属性(
TestCModel)/非可空字符串
启用NRT后,非可空引用类型被框架视为“必须有值”,模型绑定后如果属性为null(未赋值),会自动触发验证错误,无需[Required]。对于字符串,NRT仅检查是否为null,若传入空串(""),NRT验证不会报错——如果需要禁止空串,需搭配[Required(AllowEmptyStrings = false)]使用。非可空值类型(
int)
值类型本身不能为null,会被自动初始化为默认值(如int默认0),框架无法区分是用户主动传入的0还是默认值。要实现“必须由用户传入值”的验证,需将属性改为可空值类型(int?)并添加[Required]:未赋值时属性为null,[Required]会触发验证失败;用户传入0时,属性有值则验证通过。
你的核心疑问解答
类本质是可空的吗?
引用类型(包括自定义类)本身在.NET中确实可以为null,但非可空引用类型是语法层面的约定增强——通过NRT语法(去掉?),你向编译器和框架声明了该属性“不应该为null”,框架据此自动添加null验证。这不是类本身的特性,而是语言和框架提供的编译期检查+运行时验证的组合机制。
[Required]特性的意义是什么?
在NRT启用的场景下,[Required]依然有不可替代的作用:
- 强制可空引用类型必填:对于标记为
?的可空引用类型,若业务上要求必须有值,[Required]可以覆盖可空设定,强制验证非null。 - 值类型的“必填”区分:如你测试的
int,配合可空值类型使用,能区分“未赋值”和“传入默认值”的场景。 - 更细致的字符串控制:通过
AllowEmptyStrings参数,可禁止空串([Required(AllowEmptyStrings = false)]),而NRT仅检查null,不处理空串。 - 兼容旧项目:在未启用NRT的项目中,所有引用类型默认视为可空,
[Required]是实现必填验证的标准方式。 - 自定义验证场景:部分复杂模型绑定场景下,
[Required]可以明确标记必填规则,提升代码可读性。
内容的提问来源于stack exchange,提问作者Tristen Hannah

