You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:20:06