Spring MVC Rest Controller中实体校验消息不显示及错误返回HTML而非JSON问题排查
你遇到的问题是Spring MVC REST接口在参数校验失败时,返回HTML错误页面而非JSON格式的自定义校验消息,核心原因是Spring MVC默认的异常处理机制没有针对REST场景做适配,没有捕获并处理参数校验抛出的异常,而是直接转发到错误页面。下面是具体的原因解释和解决步骤:
一、核心原因
当你使用@Valid注解校验请求体时,如果校验失败,Spring会抛出MethodArgumentNotValidException异常;如果请求体为null,则会抛出HttpMessageNotReadableException。Spring MVC默认的异常解析器会将这些异常映射到错误页面(比如/error),返回HTML格式响应,而不会自动返回包含自定义校验消息的JSON。
二、解决办法
1. 添加全局异常处理器(推荐方案)
创建一个全局异常处理类,专门捕获校验相关的异常,提取自定义错误消息并封装成JSON响应返回。这种方式可以统一处理所有REST接口的校验异常,复用性强。
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.http.converter.HttpMessageNotReadableException; import java.util.HashMap; import java.util.Map; @RestControllerAdvice public class GlobalRestExceptionHandler { // 处理参数校验失败的异常 @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Map<String, String>> handleValidationErrors(MethodArgumentNotValidException ex) { Map<String, String> errorMap = new HashMap<>(); // 提取每个字段的自定义校验消息 ex.getBindingResult().getAllErrors().forEach(error -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errorMap.put(fieldName, errorMessage); }); return new ResponseEntity<>(errorMap, HttpStatus.BAD_REQUEST); } // 处理请求体为null的异常 @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity<String> handleNullRequestBody(HttpMessageNotReadableException ex) { return new ResponseEntity<>("Request body cannot be null or empty", HttpStatus.BAD_REQUEST); } }
2. 验证依赖完整性(确保已有)
你的pom.xml已经包含了必要的依赖,无需额外添加:
hibernate-validator:提供JSR-380校验规范的实现,支持@NotBlank、@Size等注解jackson-databind:负责JSON的序列化与反序列化,确保响应能以JSON格式返回
如果后续出现JSON序列化问题,可以检查版本兼容性(你的Spring 5.2.8.RELEASE与Jackson 2.9.9.2、Hibernate Validator 6.2.0.Final是兼容的)。
3. 可选:手动处理校验错误(不推荐)
如果你不想用全局异常处理器,也可以在控制器方法中添加BindingResult参数手动处理校验错误,但这种方式需要在每个校验方法中重复编写逻辑:
@PostMapping(value = "", produces="application/json") public ResponseEntity<?> createNewProject(@Valid @RequestBody Project project, BindingResult result){ // 手动检查校验结果 if(result.hasErrors()){ Map<String, String> errorMap = new HashMap<>(); result.getAllErrors().forEach(error -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errorMap.put(fieldName, errorMessage); }); return new ResponseEntity<>(errorMap, HttpStatus.BAD_REQUEST); } Project proj1 = projectService.saveOrUpdate(project); return new ResponseEntity<Project>(proj1, HttpStatus.CREATED); }
三、测试验证
完成配置后,用Postman发送无效请求(比如projectName为空、projectIdentifier长度不符合要求),会收到如下JSON格式的响应:
{ "projectName": "Project name cannot be blank", "projectIdentifier": "Please use 4-5 character identifier" }
发送null请求体时,会收到:
"Request body cannot be null or empty"
内容的提问来源于stack exchange,提问作者Yogesh Sarma

