Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jersey/Jackson Exception problem with ExceptionMapper

I'm using Jersey to provide a java REST service to the outside world. I offer some functions that take JSON and I use the Jackson framework in combination with jersey to convert them into POJOs.

I have the problem that if wrong jackson format is sent to the server, the answer (content of the http response) is a jackson specific exception description. If I have a POJO with an attribute "surname" and send "sursname" in the json string to the server, I get:

Unrecognized field "sursname" (Class XY), not marked as ignorable at [Source: sun.net.httpserver.FixedLengthInputStream@903025; line: 1, column: 49] (through reference chain: XY["sursname"])

Thats quite ok, but I would like to have my own response content, for example my own error code. I already wrote a custom ExceptionMapper that should map all Throwables.

@Provider
public class WebserviceExceptionMapper implements ExceptionMapper<Throwable> {

@Override
public Response toResponse(Exception e) {
    e.printStackTrace();
    return Response.status(400).entity("{\"errorcode\":\"CRITICAL_ERROR\"}").type(MediaType.APPLICATION_JSON).build();
    }
}

It seems that the jackson exception is thrown before my webservice method is called, so i have no chance to map it?

Does anyone have an idea? Big thanks und sorry for my english ;)

like image 860
freshy Avatar asked Apr 29 '11 14:04

freshy


3 Answers

The reason that your custom ExceptionMapper doesn't catch the exceptions is because the Jackson library provides its own exception mappers, which catches the exception before it would be catched by your genereal exception mapper.

Some suggest that you should implement your own exception mappers for JsonParseExceptionMapper and JacksonMappingExceptionMapper, but that, however, will give inconsistent results. See this issue on their GitHub.

To solve this problem, you must make sure that none of the built-in exception mappers are registered. If you're using the jackson-jaxrs-providers library for JSON: jackson-jaxrs-json-provider, make sure that you are only registering either JacksonJaxbJsonProvider.class or JacksonJsonProvider.class in your Application class:

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        register(JacksonJaxbJsonProvider.class)
    }
}

Be aware of the JacksonFeature.class, as it registers the built in ExceptionMappers. Also stay away from the jersey-media-json-jackson library, which automatically will add some built in exception mappers, without you having to do anything at all.

like image 130
Christoffer Karlsson Avatar answered Oct 21 '22 07:10

Christoffer Karlsson


I ran into a similar issue a while back while using Jackson and Apache CXF. The exception mappers weren't called if the exception didn't happen in my JAX-RS method and instead happened in the JacksonJsonProvider. The solution in my case was to extend JacksonJsonProvider, catch the specific Jackson json exceptions, and rethrow them as WebApplicationException.

Here's my overriden readFrom method for my extended JacksonJsonProvider:

    @Override
    public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
        try {
            return super.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream);
        } catch (JsonParseException jpe) {
            throw new WebApplicationException(jpe, Response.status(Status.BAD_REQUEST)
                    .entity(new ErrorEntity("Malformed json passed to server: \n" + jpe.getMessage())).build());
        } catch (JsonMappingException jme) {
            throw new WebApplicationException(jme, Response
                    .status(Status.BAD_REQUEST)
                    .entity(new ErrorEntity("Malformed json passed to server, incorrect data type used: \n"
                            + jme.getMessage())).build());
        }
    }
like image 2
Harleen Avatar answered Oct 21 '22 08:10

Harleen


Of course Jackson exception is thrown before: the Jackson provider is called in order to create an object you are expecting in your method. However, with ExceptionMapper you should be able to map not only your exceptions, but also the providers exception.

Are you sure your provider is registered? Does it being called if your throw exception from your method and not from provider?

If the answer to the previous question is "yes", try to implement ExceptionMapper for a concrete exception instead of Throwable.

like image 1
Tarlog Avatar answered Oct 21 '22 07:10

Tarlog