杰克逊反序列化失败时泽西异常映射器不工作

问题描述 投票:0回答:7

我在 REST API 中使用 Jersey 2.10 和 Jackson 序列化/反序列化功能。

我的想法是让我的 REST API 始终返回标准 JSON 错误响应。为此,我有 ExceptionMapper 类,可以为 Jersey 应用程序中抛出的任何异常构建正确的 json 错误响应。我还有一个 jsp,它会生成相同类型的 JSON 响应,我将其注册为 web.xml 中的错误页面,涵盖了加载 Jersey 之前可能出现的所有错误。

但是有一种情况,我的异常映射器和生成 json 的 jsp 都不起作用,即向 POST REST 端点发送格式错误的 json 时,该端点仅返回以下消息:

HTTP/1.1 400 Bad Request Server: Apache-Coyote/1.1 Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, DELETE, PUT Content-Type: text/plain Content-Length: 210 Date: Tue, 24 Jun 2014 22:14:11 GMT Connection: close Can not deserialize instance of com.example.rest.User[] out of START_OBJECT token at [Source: org.glassfish.jersey.message.internal.EntityInputStream@1dcccac; line: 1, column: 1]

我怎样才能让 Jersey 返回我的自定义错误响应而不是这个?

更新:

根据@Lucasz的回答,我做了更多研究,发现com.fasterxml.jackson.jaxrs.base包内定义了两个异常映射器(

https://github.com/FasterXML/jackson-jaxrs- providers/tree/master/base/src/main/java/com/fasterxml/jackson/jaxrs/base) JsonMappingExceptionMapper 和 JsonParseExceptionMapper 似乎正在隐藏我的自定义映射器。

如何取消注册这些映射器?

这就是我目前注册映射器的方式:

@ApplicationPath("/") public class MyApp extends ResourceConfig{ public SyntheticAPIApp() { packages("com.example.resource", "com.example.mapper"); register(org.glassfish.jersey.jackson.JacksonFeature.class); } }
    
java rest jersey jackson
7个回答
35
投票
我用如下所示的异常映射器对其进行了测试:

import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import com.fasterxml.jackson.core.JsonProcessingException; @Provider public class JsonProcessingExceptionMapper implements ExceptionMapper<JsonProcessingException>{ public static class Error { public String key; public String message; } @Override public Response toResponse(JsonProcessingException exception) { Error error = new Error(); error.key = "bad-json"; error.message = exception.getMessage(); return Response.status(Status.BAD_REQUEST).entity(error).build(); } }

并且成功了。


更新:将 JsonParseException 更改为 JsonProcessingException(更通用)


更新2: 为了避免注册不需要的映射器,请替换

register(org.glassfish.jersey.jackson.JacksonFeature.class);

register(com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.class);

查看

JacksonFeature 的源代码,您就会明白发生了什么。


4
投票
从 Jersey 2.26(

12)开始,用足够高的 Priority

(这里的 
high 表示一个低的、严格的正数)来注释自定义异常映射器应该足够了。为了覆盖 org.glassfish.jersey.media:jersey-media-json-jackson
 提供的“默认”映射器(到 
register(JacksonFeature.class)
),我们只提供这两个自定义映射器:

@Provider @Priority(1) public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> { /* ... */ }

@Provider @Priority(1) public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> { /* ... */ }

不幸的是,JAX-RS 2 规范无视优先级,仅声明:

选择异常映射提供者来映射异常时, 实现必须使用其泛型类型是异常的最近超类的提供者。

不注册

JacksonFeature.class

 并注册 
JacksonJaxbJsonProvider.class
,如另一个答案中提到的那样,并没有产生一致的结果。


2
投票
我遇到了同样的问题,之前的答案引导我找到了解决方案,但并没有为我分叉当前的泽西岛(2.22)。首先,我需要使用

org.glassfish.jersey.spi.ExtendedExceptionMapper

,如 
https://jersey.java.net/documentation/latest/representations.html 中所述。

此外,Jersey 正在检查异常映射器,它尽可能接近抛出的异常(来自

org.glassfish.jersey.internal.ExceptionMapperFactory

):

for (final ExceptionMapperType mapperType : exceptionMapperTypes) { final int d = distance(type, mapperType.exceptionType); if (d >= 0 && d <= minDistance) { final ExceptionMapper<T> candidate = mapperType.mapper.getService(); if (isPreferredCandidate(exceptionInstance, candidate, d == minDistance)) { mapper = candidate; minDistance = d; if (d == 0) { // slight optimization: if the distance is 0, it is already the best case, so we can exit return mapper; } } } }

因此,我需要准确地映射异常,而不是更一般的异常。

最后,我的提供商如下所示:

@Provider public final class JsonParseExceptionExceptionHandler implements ExtendedExceptionMapper<JsonParseException> { @Override public Response toResponse(final JsonParseException exception) { exception.printStackTrace(); return Response.status(Response.Status.BAD_REQUEST).entity("JSON nicht in korrektem Format.").build(); } @Override public boolean isMappable(final JsonParseException arg0) { return true; } }
    

2
投票

我使用了“jackson-jaxrs-json-provider 2.8.8”和JAX-RS 2.0

应用程序类 - 您需要注册您的 ExceptionMapper 实现类:

@ApplicationPath("pathApplication") public class ApplicationConfiguration extends Application{ @Override public Set<Class<?>> getClasses() { Set<Class<?>> resources = new HashSet<>(); resources.add(YourJAXRSClass.class); resources.add(JsonJacksonEM.class); //ExceptionMapper class implementation //others resources that you need... return resources; } }

ExceptionMapper类实现:

@Provider public class JsonJacksonEM implements ExceptionMapper<JsonParseException>{ @Override public Response toResponse(JsonParseException exception) { //you can return a Response in the way that you want! return Response.ok(new YourObject()).build(); } }
    

0
投票
我遇到了同样的问题并解决了覆盖 ExceptionMapper 的问题。完美的!我需要做但没有 100% 理解的另一件事是如何覆盖我的应用程序的 JacksonProvider(我不知道它是否与我使用的 Jersey 版本 - 2.19 有关)。这是我的 web.xml 部分,它覆盖了它:

<init-param> <param-name>jersey.config.server.provider.classnames</param-name> <param-value> com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider </param-value> </init-param>
    

0
投票
我最近遇到了这个问题,注册

com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider

的解决方案并没有多大帮助。当我深入研究时,我发现 Jersey 默认情况下 
org.glassfish.jersey.jackson.JacksonFeature
 是由 Jersey 注册的,如果依赖项 
jersey-media-json-jackson
 即使没有注册它的显式声明(不确定自动注册是从哪个版本实现的,我猜)至少从 Jersey 2.29 开始)存在于类路径中。
JacksonFeature
 依次自动注册 
JsonParseExceptionMapper
JsonMappingExceptionMapper
。由于这些默认的 JSON 异常映射器,所有与 JSON 相关的异常都不会重定向到 
custom exception mapper

幸运的是,Jersey 2.29.1 添加了对注册 JacksonFeature 的支持,而无需异常处理程序。链接

功能请求链接代码更改

@Provider public class ApplicationConfig extends Application { @Override public Set<Object> getSingletons() { Set<Object> singletons = super.getSingletons(); singletons.add(JacksonFeature.withoutExceptionMappers()); return singletons; } }
上面的代码片段将覆盖 Jersey 注册的默认 

JacksonFeature

。通过这样做,所有与 JSON 相关的异常将被重定向到应用程序中存在的自定义异常映射器。


0
投票
您可以在没有默认异常映射器的情况下注册 JacksonFeature,如下所示

register(JacksonFeature.withoutExceptionMappers());
这里是

文档

© www.soinside.com 2019 - 2024. All rights reserved.