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

Enum抛自定义异常未进入@ExceptionHandler方法的解决方案

枚举@JsonCreator抛出的自定义异常无法进入对应异常处理器的解决方法

问题相关代码

枚举类定义,在@JsonCreator标注的静态方法中做参数校验,不通过时抛出自定义CustomException:

public enum Metadata{
    AC, CD;

    @JsonCreator
    public static Metadata get(String value) throws CustomException {
        if(!isValid(value)){
            throw new CustomException();
        }
        // 省略合法值转换逻辑
        return Metadata.valueOf(value);
    }

    // 省略isValid校验方法实现
    private static boolean isValid(String value) {
        // 自定义校验逻辑
        return true;
    }
}

全局异常处理器中定义的自定义异常处理方法:

@ExceptionHandler(CustomException.class)
public ResponseEntity<Response> handleJsonError(CustomException e){
    // 省略自定义响应构造逻辑
    return response;
}

实际现象

枚举校验失败抛出CustomException时,请求不会进入上述CustomException对应的处理方法,反而进入HttpMessageNotReadableException的处理分支。调试查看异常的cause属性,可见异常被逐层包裹:最外层为HttpMessageNotReadableException,中间层是Jackson的ValueInstantiationException,最内层才是业务抛出的CustomException。

问题原因

Spring MVC解析@RequestBody请求参数时,会统一捕获Jackson反序列化阶段抛出的所有异常,包装为HttpMessageNotReadableException后再进入全局异常匹配流程。@JsonCreator抛出的异常属于Jackson反序列化异常,因此会被两层包装,外层异常类型不匹配CustomException的处理器规则,自然无法进入对应处理逻辑。

解决方法

方案1:在HttpMessageNotReadableException处理器中递归解析根异常(推荐)

该方案改动最小,不需要额外修改Jackson配置,只需要在已有的HttpMessageNotReadableException处理逻辑中,递归遍历异常cause链找到最内层根异常,如果是CustomException就直接复用对应的处理逻辑即可:

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<Response> handleHttpMessageNotReadable(HttpMessageNotReadableException e) {
    Throwable rootCause = getRootCause(e);
    if (rootCause instanceof CustomException) {
        return handleJsonError((CustomException) rootCause);
    }
    // 原有HttpMessageNotReadableException的处理逻辑
    return defaultBadRequestResponse;
}

// 递归获取异常链最内层的根异常
private Throwable getRootCause(Throwable throwable) {
    Throwable cause = throwable.getCause();
    if (cause == null) {
        return throwable;
    }
    return getRootCause(cause);
}

方案2:自定义Jackson反序列化处理器,从源头避免异常被包装

如果不想在异常处理器中做异常链遍历,可以自定义Jackson配置,注册反序列化错误处理器,在枚举解析失败时直接抛出CustomException,跳过Jackson和Spring的默认异常包装逻辑:

@Configuration
public class JacksonConfig {
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
        return builder -> builder.postConfigurer(objectMapper -> {
            objectMapper.addHandler(new DeserializationProblemHandler() {
                @Override
                public Object handleWeirdStringValue(DeserializationContext ctxt, Class<?> targetType, String valueToConvert, String failureMsg) throws IOException {
                    if (targetType.equals(Metadata.class)) {
                        throw new CustomException();
                    }
                    return super.handleWeirdStringValue(ctxt, targetType, valueToConvert, failureMsg);
                }
            });
        });
    }
}

使用该方案需要注意,枚举校验逻辑需要和@JsonCreator中的逻辑保持一致,避免出现校验规则不统一的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 04:21:31