关于异常原因的 Jersey Exception Mapper

Jersey Exception Mapper on exception cause

我正在编写一个 Jersey 应用程序,我使用 Hibernate 作为后端。我需要通过异常映射器捕获一个特定错误,该错误由底层 JDBC 驱动程序 (MySqlDataTruncation) 抛出。不幸的是,Hibernate 接受了这个异常并将它包装在一个通用的 HibernateException.

我已经有一个通用的 HibernateException 映射器,它 return 是一个 500 错误。调用它来映射所有 HibernateExceptions,包括由 MySqlDataTruncation 异常引起的。

我想注册一个新的异常映射器,它应该处理由 MySqlDataTruncation 错误和 return 400 错误引起的 HibernateExceptions。注册一个 ExceptionMapper<MySqlDataTruncation> 似乎没有捕捉到任何我想要的异常。

ExceptionMapper<HibernateExceeption> 中检查原因似乎很麻烦。这是唯一的解决方案吗?

Checking the cause in the ExceptionMapper seems hacky.

完全没有。原因是通过 public 方法提供的。这是异常概念的一部分,异常可以提供有关其原因的信息。

找出原因。如果需要,甚至可以深入几层。如果您不想在主处理程序中处理 MySqlDataTruncation,请创建一个单独的 class 并从主处理程序委托给它。

实际上,Jersey 似乎已经预测到了这个确切的问题,并创建了一个系统来有条件地处理 ExceptionMapper 中的一些异常而不是其他异常。虽然在通用异常映射器中检查原因是完全有效的,但这似乎使代码更清晰。

只需让他们的 ExceptionMapper 实现 org.glassfish.jersey.spi.ExtendedExceptionMapper 而不是通常的 javax.ws.rs.ext.ExceptionMapper 接口。

ExtendedExceptionMapper界面是这样的:

/**
 * Extension of a {@link ExceptionMapper exception mapper interface}. The exception mapping
 * providers can extend from this interface to add jersey specific functionality to these
 * providers.
 *
 * @author Miroslav Fuksa
 *
 * @param <T> A type of the exception processed by the exception mapper.
 */
public interface ExtendedExceptionMapper<T extends Throwable> extends ExceptionMapper<T> {
    /**
     * Determine whether this provider is able to process a supplied exception instance.
     * <p>
     * This method is called only on those exception mapping providers that are able to
     * process the type of the {@code exception} as defined by the JAX-RS
     * {@link ExceptionMapper} contract. By returning {@code false} this method can reject
     * any given exception instance and change the default JAX-RS exception mapper
     * selection behaviour.
     * </p>
     *
     * @param exception exception instance which should be processed.
     * @return {@code true} if the mapper is able to map the particular exception instance,
     *         {@code false} otherwise.
     */
    public boolean isMappable(T exception);
}

这个特定案例中的实现可能类似于:

public class MySqlDataTruncationExceptionMapper implements ExtendedExceptionMapper<HibernateException> {

    @Override
    public boolean isMappable(HibernateException top) {
        return top.getCause() instanceof MySqlDataTruncation;
    }

    @Override
    public Response toResponse(HibernateException top) {
        MySqlDataTruncation e = (MySqlDataTruncation) top.getCause();
        // No possibility of a ClassCastException because we already checked
        // that `top.getCause() instanceof MySqlDataTruncation`

        // Do stuff with `e` to return a response:
        return Response.status(Status.REQUEST_ENTITY_TOO_LARGE).entity(e.getMessage()).build();
    }
}

注意:我还没有实际测试过这个(我最后做了其他事情,由于时间限制现在不能改变它),以及使用这个解决方案的 YMMV。