Spring Boot @Valid校验DTO失败时抛出的实际异常类型是什么
参数校验全局异常拦截失效问题
问题描述
- 提交不符合校验规则的DTO请求时,全局异常处理器中配置的
@ExceptionHandler(ValidationException.class)方法未触发,仅兜底的Exception类型处理器可以捕获该异常,需要明确异常实际类型以编写精准匹配的处理逻辑。 - 关闭通用异常处理器后,接口返回默认400错误响应:
异常响应截图
关联代码
DTO定义
public class BookDto { @NotBlank(message = "Name of the book cannot be empty!") @Size(min = 4, max = 30, message = "Name of the book must be between 4 and 30 characters long!") public String name; @NotBlank(message = "Author of the book cannot be empty!") @Size(min = 4, max = 35, message = "Author of the book must be between 5 and 35 characters long!") public String author; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") public LocalDate publicationYear; @NotNull(message = "Pages of the book cannot be null!") @Digits(integer = 4, fraction = 0) public int pagesNumber; @NotBlank(message = "Publisher of the book cannot be empty!") @Size(min = 4, max = 30, message = "Publisher of the book must be between 4 and 30 characters long!") public String publisher; }
全局异常处理器定义
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) protected ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND); } @ExceptionHandler(ResourceWasDeletedException.class) protected ResponseEntity<MyGlobalExceptionHandler> handleDeleteException() { return new ResponseEntity<>(new MyGlobalExceptionHandler("This book was deleted!"), HttpStatus.NOT_FOUND); } @ExceptionHandler(ValidationException.class) public ResponseEntity<?> handleValidationException(ValidationException exception, WebRequest webRequest) { ErrorDetails errorDetails = new ErrorDetails(new Date(), exception.getMessage(), webRequest.getDescription(false)); return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); } /* @ExceptionHandler(Exception.class) public ResponseEntity<?> globalExceptionHandler(Exception ex, WebRequest request) { ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR); }*/ @Data @AllArgsConstructor private static class MyGlobalExceptionHandler { private String message; } }
接口定义
@PostMapping("/books") @ResponseStatus(HttpStatus.CREATED) public BookDto createBook(@Valid @RequestBody BookDto requestForSave){ var book = converter.getMapperFacade().map(requestForSave, Book.class); log.debug("Book={}", book); var bookDto = converter.toDto((Book) bookService.create(book)); log.debug("Book={}", bookDto); return bookDto; }
问题根因
该场景下@RequestBody标注的请求体参数校验失败时,Spring MVC抛出的异常类型为**org.springframework.web.bind.MethodArgumentNotValidException**,并非ValidationException,因此原有校验异常处理器无法匹配触发。
补充说明:
ValidationException的子类ConstraintViolationException仅会在@RequestParam、@PathVariable这类非请求体参数校验失败时抛出,无法覆盖@RequestBody的校验场景。
修复方案
在全局异常处理器中新增MethodArgumentNotValidException类型的异常处理方法即可,示例代码如下:
@ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<?> handleMethodArgInvalidException(MethodArgumentNotValidException ex, WebRequest webRequest) { // 提取所有字段校验错误信息 List<String> errMsgs = ex.getBindingResult() .getFieldErrors() .stream() .map(FieldError::getDefaultMessage) .collect(Collectors.toList()); ErrorDetails errorDetails = new ErrorDetails(new Date(), String.join("; ", errMsgs), webRequest.getDescription(false)); return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); }
注意:Spring Boot 2.3及以上版本需要手动引入spring-boot-starter-validation依赖,校验相关逻辑才会正常生效。
内容的提问来源于stack exchange,提问作者iank
相关产品推荐
相关产品推荐

