JAX-RS 异常映射器:包装异常和默认情况如何?
JAX-RS exception mapper: What about wrapped exceptions and default case?
在 REST 服务中处理异常的典型方法是定义自定义异常类型(通常来自 RuntimeException
),然后实现映射器 class 以生成 HTTP 代码,例如:
public class MyExceptionMapper implements ExceptionMapper<MyException> {
@Override
public Response toResponse(MyExceptionex) {
return Response.status(400).entity("bad request")
.type(MediaType.APPLICATION_JSON).build();
}
}
现在,我有两个问题:
- 我如何为 "default case" 实现映射器,这意味着每个未映射到此处或另一个映射器中的异常?例如,当为
Throwable
实现一个以生成 HTTP 500 时,它不会再次捕获我自己的异常吗?还是可以定义映射器工作的顺序?
- 当从 REST 服务调用 EJB 等托管组件时,抛出的异常不会导致
EJBException
或某些 Transaction...Exception
包装我自己的组件吗?
"default case"...
的映射器
正如 Paul Samsotha 在评论中所写,JAX-RS 服务器运行时应该选择最具体的异常映射器。或者引用 JAX-RS 规范(对于 JEE7/version 2.0):
3.3.4 Exceptions
[...]
- If an exception mapping provider (see Section 4.4) is available for the exception or one of its superclasses, an implementation MUST use the provider whose generic type is the nearest superclass of the exception to create a Response instance that is then processed according to Section 3.3.3.[...]
所以我想您可以为 Throwable
使用异常映射器 - 它的签名无论如何都会验证它:
public interface ExceptionMapper<E extends Throwable> {...}
当从 REST 服务调用 EJB 等托管组件时...
如果需要包装,EJB 容器将包装异常。并非 EJB 抛出的所有异常都需要包装。 EJB 规范 (v3.1) 区分了应用程序异常(用 javax.ejb.ApplicationException
注释)和 "all other exceptions"(请参阅第 14.3 节)。
所以创建异常 @ApplicationException
并为其提供一个映射器。但是如果你仍然想根据 wrapped 异常进行响应:
根据包装的异常响应
您不能直接select 基于包装异常的映射器。但是您 可以 为 wrapper 异常创建一个异常映射器,然后 select 为 wrapped 基于 Providers
上下文的异常(参见 JAX-RS 2.0 第 9.2.6 节和 javax.ws.rs.ext.Providers
Javadocs)。例如,假设的 MyWrapperException
的未经测试代码为:
@Provider
public class MyWrapperExceptionMapper implements ExceptionMapper<MyWrapperException> {
@Context
private Providers providers;
public Response toResponse(MyWrapperException e) {
Throwable t = e.getCause();
ExceptionMapper mapper = providers.getExceptionMapper(t.getClass());
if( mapper != null ) {
return mapper.toResponse(t);
}
else {
// custom handling...
}
}
}
在 REST 服务中处理异常的典型方法是定义自定义异常类型(通常来自 RuntimeException
),然后实现映射器 class 以生成 HTTP 代码,例如:
public class MyExceptionMapper implements ExceptionMapper<MyException> {
@Override
public Response toResponse(MyExceptionex) {
return Response.status(400).entity("bad request")
.type(MediaType.APPLICATION_JSON).build();
}
}
现在,我有两个问题:
- 我如何为 "default case" 实现映射器,这意味着每个未映射到此处或另一个映射器中的异常?例如,当为
Throwable
实现一个以生成 HTTP 500 时,它不会再次捕获我自己的异常吗?还是可以定义映射器工作的顺序? - 当从 REST 服务调用 EJB 等托管组件时,抛出的异常不会导致
EJBException
或某些Transaction...Exception
包装我自己的组件吗?
"default case"...
的映射器正如 Paul Samsotha 在评论中所写,JAX-RS 服务器运行时应该选择最具体的异常映射器。或者引用 JAX-RS 规范(对于 JEE7/version 2.0):
3.3.4 Exceptions
[...]
- If an exception mapping provider (see Section 4.4) is available for the exception or one of its superclasses, an implementation MUST use the provider whose generic type is the nearest superclass of the exception to create a Response instance that is then processed according to Section 3.3.3.[...]
所以我想您可以为 Throwable
使用异常映射器 - 它的签名无论如何都会验证它:
public interface ExceptionMapper<E extends Throwable> {...}
当从 REST 服务调用 EJB 等托管组件时...
如果需要包装,EJB 容器将包装异常。并非 EJB 抛出的所有异常都需要包装。 EJB 规范 (v3.1) 区分了应用程序异常(用 javax.ejb.ApplicationException
注释)和 "all other exceptions"(请参阅第 14.3 节)。
所以创建异常 @ApplicationException
并为其提供一个映射器。但是如果你仍然想根据 wrapped 异常进行响应:
根据包装的异常响应
您不能直接select 基于包装异常的映射器。但是您 可以 为 wrapper 异常创建一个异常映射器,然后 select 为 wrapped 基于 Providers
上下文的异常(参见 JAX-RS 2.0 第 9.2.6 节和 javax.ws.rs.ext.Providers
Javadocs)。例如,假设的 MyWrapperException
的未经测试代码为:
@Provider
public class MyWrapperExceptionMapper implements ExceptionMapper<MyWrapperException> {
@Context
private Providers providers;
public Response toResponse(MyWrapperException e) {
Throwable t = e.getCause();
ExceptionMapper mapper = providers.getExceptionMapper(t.getClass());
if( mapper != null ) {
return mapper.toResponse(t);
}
else {
// custom handling...
}
}
}