如何在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
相关产品推荐
相关产品推荐

