@ControllerAdvice未触发Jackson反序列化错误,如何处理@Valid异常?
这个问题我之前也遇到过,核心原因是Jackson反序列化异常发生在@Valid校验之前——当请求体里的LocalDate格式不对时,Spring MVC在解析请求体的阶段就抛出了异常,根本没走到控制器方法执行、@Valid校验的环节,所以你的@ControllerAdvice自然不会触发。下面给你几种实用的解决方案:
方案1:直接捕获
HttpMessageNotReadableException Jackson反序列化失败时,最终会抛出HttpMessageNotReadableException,你可以在全局异常处理器里直接处理这个异常,从中提取具体的错误信息:
@ControllerAdvice public class GlobalExceptionHandler { // 处理Jackson反序列化异常(比如LocalDate格式错误) @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity<ErrorResponse> handleDeserializationError(HttpMessageNotReadableException ex) { String errorMsg = "请求参数格式不符合要求"; // 从异常根源中提取更具体的错误信息 if (ex.getCause() instanceof JsonMappingException jsonEx) { errorMsg = jsonEx.getOriginalMessage(); // 如果是LocalDate解析错误,还可以进一步优化提示,比如替换成更友好的中文描述 if (jsonEx.getCause() instanceof DateTimeParseException) { errorMsg = "日期格式错误,请使用yyyy-MM-dd格式"; } } ErrorResponse response = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), errorMsg); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } // 你原来处理业务保存错误的方法 @ExceptionHandler(BusinessSaveException.class) public ResponseEntity<ErrorResponse> handleBusinessSaveError(BusinessSaveException ex) { // 原有业务异常处理逻辑 } } // 自定义错误返回体 record ErrorResponse(int code, String message) {}
这个方案的优点是快速生效,不需要修改实体类或序列化配置,适合大多数场景。
方案2:自定义Jackson反序列化器,抛出定制化异常
如果需要更精准的错误提示(比如明确指出哪个字段格式错误),可以给LocalDate字段定制反序列化逻辑,当格式不对时抛出自己定义的异常:
// 自定义LocalDate反序列化器 public class CustomLocalDateDeserializer extends StdDeserializer<LocalDate> { private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); public CustomLocalDateDeserializer() { super(LocalDate.class); } @Override public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException { String dateStr = parser.getText().trim(); try { return LocalDate.parse(dateStr, DEFAULT_FORMAT); } catch (DateTimeParseException e) { // 抛出自定义异常,带上字段名和错误信息 String fieldName = parser.getCurrentName(); throw new InvalidDateFormatException(fieldName, dateStr); } } } // 自定义异常 public class InvalidDateFormatException extends RuntimeException { public InvalidDateFormatException(String fieldName, String inputValue) { super(String.format("字段【%s】格式错误,输入值为:%s,请使用yyyy-MM-dd格式", fieldName, inputValue)); } }
然后在实体类的LocalDate字段上标注这个反序列化器:
public class UserRequest { @JsonDeserialize(using = CustomLocalDateDeserializer.class) private LocalDate birthday; // 其他字段... }
最后在全局异常处理器里处理这个自定义异常:
@ExceptionHandler(InvalidDateFormatException.class) public ResponseEntity<ErrorResponse> handleInvalidDateFormat(InvalidDateFormatException ex) { ErrorResponse response = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage()); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); }
这个方案的优点是错误提示更精准,适合对用户体验要求较高的场景。
方案3:全局配置Jackson日期格式(辅助优化)
如果你的项目只接受固定格式的日期,可以先在Spring Boot配置里全局统一Jackson的日期解析格式,减少不必要的异常:
# application.properties spring.jackson.date-format=yyyy-MM-dd spring.jackson.time-zone=GMT+8 spring.jackson.serialization.write-dates-as-timestamps=false
不过这个配置只是统一格式,用户传入错误格式时还是会抛出异常,所以还是需要结合方案1或方案2来处理异常。
补充:区分两类异常
最后再明确下两类异常的区别,避免混淆:
- Jackson反序列化异常:请求体解析阶段抛出(比如LocalDate格式错),对应
HttpMessageNotReadableException - @Valid校验异常:请求体解析完成后,字段校验失败抛出(比如@NotNull字段为空),对应
MethodArgumentNotValidException
如果你的@ControllerAdvice还没处理后者,也可以加上对应的处理器,这样就能覆盖所有参数相关的错误场景了。
内容的提问来源于stack exchange,提问作者cicit
相关产品推荐
相关产品推荐

