@Valid 和 @Validated 有什么区别?为什么我写了 @NotBlank 却没生效?分组到底查哪些约束?嵌套对象怎么校验?校验失败时客户端能看到什么?这篇在 Spring Boot 4.0.8 上逐个测量。
POST /v/body {"name": "Tom", "age": 20, "addr": {"city": "BJ"}}→ 200 body User[id=null, name=Tom, age=20, addr=Address[city=BJ]]
Controller 方法被调用:body User[id=null, name=Tom, age=20, addr=Address[city=BJ]]
POST /v/body {"name": " ", "age": 10, "addr": {"city": ""}}→ 400 [MethodArgumentNotValidException] addr.city=city 不能为空; age=age 至少 18; name=name 不能为空
POST /v/body-nobind {"name": " ", "age": 10, "addr": {"city": ""}}→ 200 bodyNo(没有 @Valid) User[id=null, name= , age=10, addr=Address[city=]]
Controller 方法被调用:bodyNo(没有 @Valid) User[id=null, name= , age=10, addr=Address[city=]]
POST /v/body-binding {"name": " ", "age": 10, "addr": {"city": ""}}→ 200 BindingResult 错误数=3 [addr.city, age, name]
Controller 方法被调用:BindingResult 错误数=3 [addr.city, age, name]@Valid 触发校验,失败抛异常,Controller 不被调用;BindingResult 可以自己处理。
GET /v/param?page=0&q=%20 → 400 [HandlerMethodValidationException] page 至少 1; q 不能为空
GET /v/path/0 → 400 [HandlerMethodValidationException] id 至少 1
GET /vc/param?page=0 → 500 [ConstraintViolationException] page 至少 1
GET /v/svc?n=0 → 500 [ConstraintViolationException] n 至少 1
GET /v/plain?n=0 → 200 plain ok 0
Controller 方法被调用:plain ok 0内置方法校验返回 400;控制器类加 @Validated 会走 AOP 变成 500;Service 必须类上有 @Validated。
POST /v/g-create {"id": 5, "name": "", "age": 1}→ 400 [MethodArgumentNotValidException] id=创建时 id 必须为空
POST /v/g-create {"name": "", "age": 1}→ 200 create User[id=null, name=, age=1, addr=null]
Controller 方法被调用:create User[id=null, name=, age=1, addr=null]
POST /v/g-update {"name": "", "age": 1}→ 400 [MethodArgumentNotValidException] id=更新时 id 不能为空
POST /v/g-both {"id": 5, "name": "", "age": 1}→ 400 [MethodArgumentNotValidException] age=age 至少 18; id=创建时 id 必须为空; name=name 不能为空指定分组后默认组不参加,漏掉的约束会静默失效。
POST /v/nested1 {"no": "a", "addr": {"city": ""}}→ 200 nested1 Order1[no=a, addr=Address[city=]]
Controller 方法被调用:nested1 Order1[no=a, addr=Address[city=]]
POST /v/nested2 {"no": "a", "addr": {"city": ""}}→ 400 [MethodArgumentNotValidException] addr.city=city 不能为空每一层嵌套都要写 @Valid。
POST /v/body {"name": " ", "age": 10, "addr": {"city": ""}}→ 400 {"status":400,"error":"Bad Request","path":"/v/body"}
GET /v/param?page=0&q=%20 → 400 {"status":400,"error":"Bad Request","path":"/v/param"}
GET /vc/param?page=0 → 500 {"status":500,"error":"Internal Server Error","path":"/vc/param"}
GET /v/svc?n=0 → 500 {"status":500,"error":"Internal Server Error","path":"/v/svc"}Boot 默认不带违规信息。
POST /v/body {"name": " ", "age": 10, "addr": {"city": ""}}→ 400 {"detail":"Invalid request content.","instance":"/v/body","status":400,"title":"Bad Request"}
GET /v/param?page=0&q=%20 → 400 {"detail":"Validation failure","instance":"/v/param","status":400,"title":"Bad Request"}开关只给概括,字段级信息要自己放进响应。