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"...

的映射器

正如 Paul Samsotha 在评论中所写,JAX-RS 服务器运行时应该选择最具体的异常映射器。或者引用 JAX-RS 规范(对于 JEE7/version 2.0):

3.3.4 Exceptions

[...]

  1. 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...
        }
    }
}