Spring Boot中@Valid未返回自定义校验消息问题排查
问题场景
我在Spring Boot项目中编写了以下代码:
Controller方法
@PostMapping("/hello") public Hello hello(@Valid @RequestBody Hello hello) { return hello; }
POJO类
import jakarta.validation.constraints.NotBlank; class Hello{ @NotBlank(message = "msg must be present") String msg; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
当使用空字符串的msg请求接口时:
{ "msg":"" }
得到的响应是:
{ "type": "about:blank", "title": "Bad Request", "status": 400, "detail": "Invalid request content.", "instance": "/hello" }
预期应该返回自定义提示msg must be present,但未生效。
已尝试的操作:
- 在
application.properties添加配置:server.error.include-message: always - 添加了
ConstraintViolationException的异常处理:
@ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException e) { return new ResponseEntity<Object>("ConstraintViolationException", HttpStatus.BAD_REQUEST); }
发现移除项目中的@RestControllerAdvice后,校验消息能正常返回,但该组件是项目自定义异常处理必需的,需要解决保留@RestControllerAdvice同时返回自定义校验消息的问题。
解决方案
问题出在全局异常处理器没有处理RequestBody参数校验抛出的MethodArgumentNotValidException,你之前只处理了ConstraintViolationException(该异常通常用于URL参数、请求参数的校验,而非RequestBody的JSON参数校验)。
在你的@RestControllerAdvice类中添加针对MethodArgumentNotValidException的处理方法,提取校验失败的自定义消息即可:
@RestControllerAdvice public class GlobalExceptionHandler { // 保留原有的ConstraintViolationException处理(若需保留) @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException e) { String message = e.getConstraintViolations().stream() .map(violation -> violation.getMessage()) .findFirst() .orElse("参数校验失败"); return new ResponseEntity<>(message, HttpStatus.BAD_REQUEST); } // 添加RequestBody参数校验的异常处理 @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { // 提取第一个校验失败的自定义消息 String message = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .findFirst() .orElse("请求体参数校验失败"); return new ResponseEntity<>(message, HttpStatus.BAD_REQUEST); } // 其他自定义异常处理方法... }
如果需要返回和默认格式一致的结构化响应,可以封装错误响应类:
class ErrorResponse { private String type = "about:blank"; private String title = "Bad Request"; private int status = 400; private String detail; private String instance; public ErrorResponse(String detail, String instance) { this.detail = detail; this.instance = instance; } // getter/setter }
修改异常处理方法返回结构化响应:
@ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { String message = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .findFirst() .orElse("请求体参数校验失败"); ErrorResponse response = new ErrorResponse(message, request.getRequestURI()); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); }
这样既保留了@RestControllerAdvice的自定义异常处理能力,又能正确返回@NotBlank中定义的提示消息。
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

