结论:用DataAnnotations特性最省事,但需确保模型由框架自动绑定;[Required]对int/DateTime无效因其有默认值,应改用[Range]或声明为可空类型;验证失败时ModelState.AddModelError仅用于业务规则,结构验证由框架自动完成。

直接说结论:用 [Required]、[StringLength] 这类 DataAnnotations 特性最省事,但必须让 ASP.NET Core 或 MVC 的模型绑定机制真正“看到”你的对象——不能手动 new User() 再赋值,否则验证完全不触发。
为什么 [Required] 对 int 和 DateTime 总是通过?
因为 int 默认值是 0,DateTime 默认是 DateTime.MinValue,它们永远“有值”。[Required] 只对可空类型(int?、DateTime?)或引用类型(string)起效。
- 想校验非空数值字段?改用
[Range(1, 100)]或[GreaterThan(0)](需 FluentValidation) - 想强制传日期?把属性声明为
DateTime?,再加[Required] - 前端传了
"age": null,后端却是int age = 0—— 这是 JSON 反序列化默认行为,不是验证失效
控制器里怎么确保验证真的跑起来了?
关键看参数绑定方式:必须用 [FromBody]、[FromForm] 或路由/查询参数自动绑定,而不是在 Action 里自己 new 对象。
- ✅ 正确:
public IActionResult Create([FromBody] User user)—— 框架会调用ModelState.IsValid - ❌ 错误:
public IActionResult Create() { var user = new User(); user.Name = Request.Form["name"]; ... }——DataAnnotations完全没机会运行 - 若用了自定义构造函数或
private set,确保属性有public get,否则验证器读不到值
验证失败时,ModelState.AddModelError 有用吗?
有用,但只在你手动干预验证流程时才需要。默认情况下,只要参数类型匹配且用了正确绑定方式,框架会在进入 Action 前就填充 ModelState。
- 不要在 Action 开头写
if (!ModelState.IsValid) return BadRequest(ModelState);—— 这是冗余的,除非你要改返回格式 - 想补充业务规则错误?在 Action 里用
ModelState.AddModelError("Email", "该邮箱已被注册") - 注意键名要和属性名一致(区分大小写),否则前端收不到对应字段错误
最容易被忽略的一点:验证特性只检查数据“结构”,不查数据库唯一性、权限、状态流转等业务约束。这些必须放在服务层,用显式判断 + ModelState.AddModelError 或抛异常来处理。


















