【问题标题】:How to override a built-in exception mapper in Jersey 2.23?如何覆盖 Jersey 2.23 中的内置异常映射器?
【发布时间】:2016-07-31 08:05:33
【问题描述】:

在我的一个项目中,我已经将 Jersey 从版本 2.14 升级到了 2.23。但我在一个问题上苦苦挣扎了好几个小时。我的项目为ValidationException 定义了自己的ExceptionMapper,但不幸的是,Jersey 已经为这个异常提供了一个内置的异常映射器,我无法覆盖它。

我已经正确注册(我检查过)我自己的映射器,如下所示:

@Provider
public class ValidationExceptionMapper implements 
         ExceptionMapper<ValidationException> {

    @Override
    public Response toResponse(ValidationException exception) {
        return Response.status(Status.BAD_REQUEST).build();
    }
}

但它永远不会被调用。泽西岛总是拿起org.glassfish.jersey.server.validation.internal.ValidationExceptionMapper。 我也尝试为我的自定义映射器使用 @Priority 注释,但不幸的是 Jersey 没有考虑到它。

那么发生了什么?它在以前的 Jersey 版本中运行良好,所以它似乎是一个回归错误。

我放弃了。有什么线索吗?

【问题讨论】:

  • 您可以使用ConstraintViolationException(这是验证异常的实际类型)作为解决方法。它比ValidationException 更具体,因此优先。我从来没有想出如何禁用那个映射器,而不禁用 all 元信息提供程序(不是一个有趣的解决方案)。我的解决方法是简单地将映射器用于ConstraintViolationException
  • 谢谢,我知道,但这根本不能解决问题。因为应用程序必须响应ValidationExceptions。
  • ConstraintViolationException 扩展 ValidationException。 Jersey 中的 bean 验证抛出的异常类型将始终是 ConstraintViolationException。它适用于ValidationException,因为超类型映射器可以处理子类型异常,是否没有子类型的映射器
  • 如果你在java.net/jira/browse/JERSEY 提交了一个关于这个回归的错误,那就太棒了。如果您添加一个简单的可重现案例,那就更好了。谢谢!
  • @peeskillet 原来是一个回归错误。可惜你没有早点报告。也许它会修复到现在。无论如何问候。

标签: java jersey jersey-2.0 embedded-jetty weld2


【解决方案1】:

这确实是 2015 年 1 月引入的 Jersey 中的回归错误。

Bug 与 Jersey 的两个扩展有关:焊接和 bean 验证。 因为没有 Weld 容器启动,我的自定义 ValidationExceptionMapper 映射器优先于 jersey-bean-validation 模块提供的内置映射器,所以我的目标实现了。

我在 JERSEY-3153 下填写了一份错误报告,后来移到了issue #3425

说实话,我再也不会使用 Weld + Jersey 了……我已经厌倦了这种组合。在过去的两年中,我已经遇到了大约 10 个错误。我真的很累。

无论如何,我希望它会对某人有所帮助。

更新: 正如@Justin Jose 在下面的 cmets 中注意到的那样,对于提到的错误还有另一种解决方法。我们可以使用 HK2 绑定来覆盖有问题的内置映射器:

register(new AbstractBinder() {
    @Override
    protected void configure() {
        bind(my.custom.ValidationExceptionMapper.class).to(ExceptionMapper.class)
               .in(Singleton.class);
    }
});

【讨论】:

  • 2k19,Jersey 2.27 - 这个错误仍然存​​在......这个解决方法仍然有效,谢谢!顺便说一句,新的 bug 主页:github.com/eclipse-ee4j/jersey/issues/3425
  • 另外请注意,虽然这个解决方案有效,但我发现它有时不起作用。 :-) 我认为,绑定标准和自定义ExceptionMapper 的优先级是随机的。我在其他答案中提到的绑定代码末尾添加了.ranked(10‌​);,正如我所见,它现在工作稳定(自定义映射器优先)。
【解决方案2】:

Jersey 内置的 ValidationExceptionMapper 是通过 ValidationFeature 注册的。或许,用您自己的版本替换 Jersey 的 ValidationFeature 可以解决问题。可以按如下方式进行。

首先,禁用自动发现的 ValidationFeature

property(ServerProperties.BV_FEATURE_DISABLE, true);

下一步是注册 Jersey 验证功能的克隆

public static class ValidationFeatureClone implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        context.register(new ValidationBinder());
        context.register(NewValidationExceptionMapper.class);
        context.register(ValidationErrorMessageBodyWriter.class);
        return true;
    }
}

在克隆中,您应该指定新的 ExceptionMapper。

最后,注册你的新功能

register(ValidationFeatureClone.class)

更新:

从 Jersey 2.20 开始,默认 ValidationExceptionMapper 可以使用 HK2 绑定覆盖,如下所示。

register(new AbstractBinder() {
    @Override
    protected void configure() {

       bind(NewValidationExceptionMapper.class).to(ExceptionMapper.class)
           .in(Singleton.class).ranked(10‌​);
    }
});

【讨论】:

  • 请仔细检查您的解决方案。我在一分钟前用 Jersey 2.23.1 进行了检查,它根本没有解决原来的问题。除此之外,ValidationErrorMessageBodyWriter 不是公开的类。
  • 我在我们的应用程序中使用 Jersey 2.19,该解决方案对我们有效。看起来从 2.20 开始,ValidationErrorMessageBodyWriter 已更改为包私有类。我没有注意到您在原始问题中提到 2.23 。您可以尝试创建ValidationErrorMessageBodyWriter 的克隆吗?
  • 看起来在最新版本中可以使用 HK2 绑定注册自定义ValidationExceptionMapper,并且不需要像我在原始答案中提到的那样注册验证功能的克隆。请尝试添加register(new AbstractBinder() { @Override protected void configure() { bind(NewValidationExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class).ranked(10); } });
  • 您使用 HK2 的解决方法有效。谢谢贾斯汀。我将更新JERSEY-3153 中的“已知解决方法”部分。请在上面更新您的答案:)
【解决方案3】:

我找到了一种让它再次与较新的 Jersey 版本一起工作的方法,我也在你的错误报告下发布了它。

需要使用更改后的代码在本地构建 Jersey,特别是 jersey-bean-validation 工件。

找到org.glassfish.jersey.server.validation.internal.ValidationBinder并注释掉configure()中的以下两行:

bind(ValidationExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
bind(ValidationErrorMessageBodyWriter.class).to(MessageBodyWriter.class).in(Singleton.class);

具有讽刺意味的是,这些行上方的源代码注释说它们应该允许用户注册自己的提供程序。

【讨论】:

    【解决方案4】:

    不幸的是,该错误仍然存​​在并导致头痛... 就我而言,最简单的解决方案是专门为 ConstraintViolationException 提供自定义 ExceptionMapper。

    public class CVExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
       @Override
       public Response toResponse(final Throwable t) {
       ...
       }
    }
    

    然后像往常一样注册它: context.register(CVExceptionMapper.class);

    【讨论】:

      猜你喜欢
      • 2019-08-26
      • 2015-03-14
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多