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

如何在Jersey Client中直接抛出自定义ShopException而非ProcessingException

解决Jersey客户端自定义异常被ProcessingException包装的问题

这个问题我之前也碰到过,核心原因是Jersey客户端运行时只会直接传播WebApplicationException及其子类,其他自定义异常都会被自动包装成ProcessingException。下面给你两种可行的解决方案:

方案一:让自定义异常继承WebApplicationException

最简单的处理方式是修改你的ShopException,让它继承javax.ws.rs.WebApplicationException——这是JAX-RS规范定义的异常类型,客户端运行时会直接传播它,不会额外包装。

修改后的ShopException示例:

public class ShopException extends WebApplicationException {
    private final ShopExceptionData data;

    public ShopException(int status, ShopExceptionData data) {
        // 将HTTP状态码传递给父类构造器
        super(status);
        this.data = data;
    }

    // 提供获取异常数据的方法
    public ShopExceptionData getData() {
        return data;
    }
}

你的原有过滤器代码不需要改动,现在抛出的ShopException会直接被客户端调用代码捕获,不会被包装成ProcessingException。

方案二:使用ResponseExceptionMapper(更符合JAX-RS扩展规范)

如果不想修改ShopException的继承关系,可以使用Jersey提供的ResponseExceptionMapper接口,它专门用于将HTTP响应映射为自定义异常,这种方式更优雅,也更贴合JAX-RS的扩展设计。

步骤1:实现ResponseExceptionMapper

创建一个映射器类,将4xx/5xx响应转换为ShopException:

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.client.spi.ResponseExceptionMapper;

// 添加@Provider注解,让Jersey能自动发现这个映射器
@Provider
public class ShopExceptionMapper implements ResponseExceptionMapper<ShopException> {

    @Override
    public ShopException toThrowable(Response response) {
        // 仅处理4xx和5xx状态码的响应
        if (response.getStatus() >= 400 && response.getStatus() < 600) {
            ShopExceptionData data = response.readEntity(ShopExceptionData.class);
            return new ShopException(response.getStatus(), data);
        }
        // 返回null表示不处理该响应,交给其他逻辑处理
        return null;
    }
}

步骤2:注册映射器到客户端

替换原有过滤器的注册代码,改为注册这个映射器:

target = ClientBuilder.newBuilder()
        .register(ShopApplication.newJacksonJsonProvider())
        .register(ShopExceptionMapper.class) // 注册自定义异常映射器
        .build()
        .target(Main.BASE_URI.resolve("products"));

这样当客户端收到4xx/5xx响应时,Jersey会自动调用ShopExceptionMapper将响应转换为ShopException,并直接抛出,不会被包装。

验证效果

你的原有测试代码不需要任何修改,现在执行测试时,catch (ShopException e)块会直接捕获到自定义异常,断言逻辑可以正常执行:

@Test 
public void getProduct_invalidId() {
    try {
        target.path("123!").request(APPLICATION_JSON).get(Product.class);
        fail("GET should raise an exception");
    } catch (ShopException e) {
        assertThat(e.getData().getErrorCode()).isEqualTo(ShopError.PRODUCT_ID_INVALID.code);
        assertThat(e.getData().getErrorMessage()).isEqualTo(ShopError.PRODUCT_ID_INVALID.message);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:10:53