@requestbody校验失败抛methodargumentnotvalidexception,@modelattribute抛bindexception,因解析流程和校验时机不同,需分别捕获处理,且未加@valid时前者可能抛httpmessagenotreadableexception。

校验失败时,@RequestBody 和 @ModelAttribute 触发的异常类型不同,根本原因在于它们走的参数解析流程和校验触发时机不一致。
校验失败抛出的异常类型不同
当使用 @RequestBody 且开启校验(如 @Valid)时,Spring 会通过 RequestResponseBodyMethodProcessor 解析请求体,并在校验失败时抛出 MethodArgumentNotValidException。该异常专用于 @RequestBody 绑定 + 校验失败场景,包含详细的 BindingResult 错误信息。
而 @ModelAttribute(或无注解的表单对象参数)走的是 ModelAttributeMethodProcessor 流程,校验失败时抛出的是 BindException。它同样携带 BindingResult,但异常层级和处理入口与前者不同。
全局异常处理器需分别捕获
因为两种异常不是父子关系,不能靠捕获一个父类覆盖两者,必须显式分开处理:
-
@ExceptionHandler(MethodArgumentNotValidException.class)处理 JSON 提交校验失败 -
@ExceptionHandler(BindException.class)处理表单提交(@ModelAttribute或默认绑定)校验失败
若只写一个处理器,另一类错误将落入默认 Spring 错误页或 500 响应,前端得不到结构化错误提示。
错误响应内容结构可统一,但获取方式有差异
两者都可通过 BindingResult 获取字段级错误,但提取方式略有不同:
-
MethodArgumentNotValidException:调用getBindingResult()直接获取 -
BindException:本身就是BindingResult的子类,可直接强转或调用getBindingResult()
建议在异常处理器中统一提取 fieldErrors,组装为 {"code":400,"errors":[{"field":"email","message":"邮箱格式不正确"}]} 这类标准格式,避免前端区分处理逻辑。
注意 @ModelAttribute 不校验时不会抛异常,@RequestBody 会因类型转换失败抛其他异常
如果没加 @Valid,@ModelAttribute 绑定失败(如字符串转数字出错)默认填充 null 或初始值,不抛校验异常;但 @RequestBody 在反序列化阶段就可能因 JSON 格式错误、字段类型不匹配等,抛出 HttpMessageNotReadableException —— 这属于解析异常,不是校验异常,需额外用 @ExceptionHandler(HttpMessageNotReadableException.class) 捕获并友好提示。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











