如何覆盖 Jersey 2.23 中的内置异常映射器?
How to override a built-in exception mapper in Jersey 2.23?
在我的一个项目中,我已经将 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 版本中工作得很好,所以它似乎是一个回归错误。
我放弃了。有什么线索吗?
原来是 2015 年 1 月引入的 Jersey 中的回归错误。
错误与 Jersey 的两个扩展有关:用于 Weld 和 bean 验证。
因为没有启动 Weld 容器,我的自定义 ValidationExceptionMapper
映射器优先于 jersey-bean-validation
模块提供的内置映射器,所以我的目标达到了。
我在 JERSEY-3153, later moved as the issue #3425 下填写了错误报告。
老实说,我再也不会使用 Weld + Jersey 了……我已经厌倦了这种组合。在过去的两年里,我已经遇到了大约 10 个错误。我真的很累。
总之,希望对大家有所帮助。
更新:
正如@Justin Jose 在下面的评论中注意到的那样,对于上述错误还有另一种解决方法。我们可以使用 HK2 绑定来覆盖有问题的内置映射器:
register(new AbstractBinder() {
@Override
protected void configure() {
bind(my.custom.ValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class);
}
});
我找到了一种方法让它再次与新的 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);
具有讽刺意味的是,这些行上方的源代码注释说他们应该允许用户注册他们自己的提供商。
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 开始,可以使用 HK2 绑定覆盖默认值 ValidationExceptionMapper
,如下所示。
register(new AbstractBinder() {
@Override
protected void configure() {
bind(NewValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class).ranked(10);
}
});
不幸的是,这个错误仍然存在并且令人头疼......
在我的例子中,最简单的解决方案是专门为 ConstraintViolationException 提供自定义 ExceptionMapper。
public class CVExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(final Throwable t) {
...
}
}
然后一如既往地注册它:
context.register(CVExceptionMapper.class);
在我的一个项目中,我已经将 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 版本中工作得很好,所以它似乎是一个回归错误。
我放弃了。有什么线索吗?
原来是 2015 年 1 月引入的 Jersey 中的回归错误。
错误与 Jersey 的两个扩展有关:用于 Weld 和 bean 验证。
因为没有启动 Weld 容器,我的自定义 ValidationExceptionMapper
映射器优先于 jersey-bean-validation
模块提供的内置映射器,所以我的目标达到了。
我在 JERSEY-3153, later moved as the issue #3425 下填写了错误报告。
老实说,我再也不会使用 Weld + Jersey 了……我已经厌倦了这种组合。在过去的两年里,我已经遇到了大约 10 个错误。我真的很累。
总之,希望对大家有所帮助。
更新: 正如@Justin Jose 在下面的评论中注意到的那样,对于上述错误还有另一种解决方法。我们可以使用 HK2 绑定来覆盖有问题的内置映射器:
register(new AbstractBinder() {
@Override
protected void configure() {
bind(my.custom.ValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class);
}
});
我找到了一种方法让它再次与新的 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);
具有讽刺意味的是,这些行上方的源代码注释说他们应该允许用户注册他们自己的提供商。
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 开始,可以使用 HK2 绑定覆盖默认值 ValidationExceptionMapper
,如下所示。
register(new AbstractBinder() {
@Override
protected void configure() {
bind(NewValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class).ranked(10);
}
});
不幸的是,这个错误仍然存在并且令人头疼...... 在我的例子中,最简单的解决方案是专门为 ConstraintViolationException 提供自定义 ExceptionMapper。
public class CVExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(final Throwable t) {
...
}
}
然后一如既往地注册它: context.register(CVExceptionMapper.class);