当杰克森反序列化失败时,Jersey异常映射器无法正常工作

时间:2014-06-24 22:53:23

标签: java rest jersey jackson

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

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

但有一种情况,我的Exception mappers和我的json生成jsp都没有工作,就是当一个坏的形成的json发送到POST REST端点时,它只返回以下消息:

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);
    }
}

6 个答案:

答案 0 :(得分:27)

我使用如下的异常映射器对其进行了测试:

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(更一般)


<强> UPDATE2: 为了避免注册不需要的映射器替换

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

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

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

答案 1 :(得分:2)

我遇到了同样的问题,之前的回答让我得到了解决方案,但并没有让我当前的泽西(2.22)。首先,我需要使用https://jersey.java.net/documentation/latest/representations.html中描述的org.glassfish.jersey.spi.ExtendedExceptionMapper

此外,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 :(得分:1)

我使用“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();
   }


}

答案 3 :(得分:0)

我遇到了同样的问题并解决了覆盖ExceptionMapper的问题。完善!我需要做的另一件事是,不理解100%是如何为我的应用程序覆盖JacksonProvider(我不知道它是否与我使用的泽西版本相关 - 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>

答案 4 :(得分:0)

从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并没有带来一致的结果。

答案 5 :(得分:0)

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

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

@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 相关的异常都将被重定向到应用程序中存在的自定义异常映射器。