Jersey Exception mappers not working when jackson deserialization fails

后端 未结 5 2010
借酒劲吻你
借酒劲吻你 2020-12-29 06:57

I am using Jersey 2.10 with Jackson serialization/deserialization feature in my REST API.

My idea is to make my REST API to always return a standard JSON error resp

相关标签:
5条回答
  • 2020-12-29 07:29

    I used "jackson-jaxrs-json-provider 2.8.8" and JAX-RS 2.0

    Application class - you needs to register your ExceptionMapper implementation class:

    @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 class implementation:

    @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 讨论(0)
  • 2020-12-29 07:33

    I had the same problem and solve overriding the ExceptionMapper. Perfect! One extra thing that I needed to do and were not understanding 100% was how to override the JacksonProvider for my application (I don't know if it was related to Jersey's version that I was using - 2.19). Here's my web.xml part that overrides it:

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
           <param-value>
              com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
           </param-value>
    </init-param>
    
    0 讨论(0)
  • 2020-12-29 07:39

    I had the same problem, and the previous answer led me to the solution, but was not forking for me current Jersey (2.22). At first, I needed to use the org.glassfish.jersey.spi.ExtendedExceptionMapper like described in https://jersey.java.net/documentation/latest/representations.html.

    Furthermore, Jersey is checking for an exception mapper, which is as close as possible to the thrown exception (from 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;
                    }
                }
            }
        }
    

    Therefore I needed to map exactly the exception and not a more general exception.

    In the end, my provider looks as follows:

    @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;
        }
    }
    
    0 讨论(0)
  • 2020-12-29 07:40

    Starting with Jersey 2.26 (1, 2) it should be enough to annotate the custom exception mapper with a sufficiently high Priority (high here meaning a low, strictly positive number). To override the “default” mappers provided by org.glassfish.jersey.media:jersey-media-json-jackson (to register(JacksonFeature.class)) we only provide these two custom mappers:

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

    Unfortunately JAX-RS 2 Spec disregards priorities and only states:

    When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.

    Not registering JacksonFeature.class and registering JacksonJaxbJsonProvider.class instead as mentioned in another answer did not lead to consistent results.

    0 讨论(0)
  • 2020-12-29 07:54

    I tested it with an exception mapper like below:

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

    and it worked.


    Update: changed JsonParseException to JsonProcessingException (more general)


    Update2: In order to avoid registering the unwanted mappers replace

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

    with

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

    Look at the source code of JacksonFeature and you'll understand what's happening.

    0 讨论(0)
提交回复
热议问题