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

Jackson未抛出自定义异常求助:日期反序列化异常未被正确捕获

问题:自定义LocalDate反序列化异常未被全局处理器捕获,返回通用400错误

相关代码

实体类字段定义

@NotNull
@JsonDeserialize(using = LocalDateDeserializer.class)
@Schema(description = "BIRTH DATE", example = "1992-02-15")
private LocalDate birthDate;

自定义Jackson反序列化器

public class LocalDateDeserializer extends StdDeserializer<LocalDate> {

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    protected LocalDateDeserializer() {
        super(LocalDate.class);
    }

    @Override
    public LocalDate deserialize(
            final JsonParser jsonParser,
            final DeserializationContext deserializationContext)
            throws IOException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        String date = node.asText();

        try {
            return LocalDate.parse(date, DATE_TIME_FORMATTER);
        } catch (Exception e) {
            throw new InvalidDateFormatException("Invalid format" + date);
        }
    }
}

自定义异常类

public class InvalidDateFormatException extends RuntimeException {
    public InvalidDateFormatException(final String message) {
        super(message);
    }
}

全局异常处理器

@ControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {

    public static final String MISSING_HEADER_MESSAGE = "{0} header must be informed";

    @ExceptionHandler(InvalidDateFormatException.class)
    public ResponseEntity<String> handleInvalidDateFormatException(InvalidDateFormatException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

问题现象

发送格式错误的birthDate请求时,代码进入反序列化器并捕获到异常,但自定义错误未生效,仅返回通用400错误。


解决方案

核心原因:Jackson反序列化阶段抛出的自定义异常会被Spring的HttpMessageConverter包装成HttpMessageNotReadableException,而继承ResponseEntityExceptionHandler的全局处理器默认会处理这个包装后的异常,导致自定义异常的处理器无法触发。

方式一:修改反序列化器,使用Jackson内置异常处理机制

调整反序列化器的异常抛出逻辑,利用Jackson的DeserializationContext抛出规范异常,让Spring能正确识别错误信息:

@Override
public LocalDate deserialize(
        final JsonParser jsonParser,
        final DeserializationContext deserializationContext)
        throws IOException {
    JsonNode node = jsonParser.getCodec().readTree(jsonParser);
    String date = node.asText();

    try {
        return LocalDate.parse(date, DATE_TIME_FORMATTER);
    } catch (Exception e) {
        // 用Jackson的方法抛出格式错误异常
        return deserializationContext.handleWeirdStringValue(LocalDate.class, date,
                "Invalid format: " + date);
    }
}

然后在全局处理器中重写handleHttpMessageNotReadable方法,返回自定义响应:

@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
                                                              HttpHeaders headers,
                                                              HttpStatus status,
                                                              WebRequest request) {
    Throwable cause = ex.getCause();
    if (cause instanceof JsonMappingException) {
        // 提取Jackson抛出的错误信息返回
        return new ResponseEntity<>(cause.getMessage(), HttpStatus.BAD_REQUEST);
    }
    // 其他情况沿用默认处理
    return super.handleHttpMessageNotReadable(ex, headers, status, request);
}

方式二:在全局处理器中处理包装后的异常

无需修改反序列化器,直接在全局处理器中新增对HttpMessageNotReadableException的处理,提取根异常判断是否为自定义异常:

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<String> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
    Throwable rootCause = ex.getRootCause();
    if (rootCause instanceof InvalidDateFormatException) {
        return new ResponseEntity<>(rootCause.getMessage(), HttpStatus.BAD_REQUEST);
    }
    return new ResponseEntity<>("Invalid request body", HttpStatus.BAD_REQUEST);
}

内容的提问来源于stack exchange,提问作者Jossany Moura

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:40:35