Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom ExceptionMapper for Jersey not working for invalid JSON input

I have the following resource that consumes a JSON being mapped to a POJO.

@Path("example")
public class ExampleResource {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response addThesis(MyObject myObject) {
        return Response.ok().entity("Test").build();
    }
}

Here's the POJO class:

public class MyObject {
    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

When I send a POST request with the body {"title":"Test title"} everything works fine. The response is Test, as expected. However, when I change the request to {"titlee":"Test title"} the server replies with this:

Unrecognized field "titlee" (class com.my.package.MyObject), not marked as ignorable (one known property: "title"]) at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@6dc6a46a; line: 2, column: 11] (through reference chain: com.my.package.MyObject["titlee"])

Obviously this is an exception thrown and returned by Jersey. How can I intercept this exception and return a custom status code and message?

What I've tried so far is to implement my own ExceptionMapper:

@Provider
public class MyJsonExceptionMapper implements ExceptionMapper<JsonProcessingException> {
    public Response toResponse(JsonProcessingException e) {
        return Response.status(400).entity("JSON Processing Error").build();
    }
}

Unfortunately the response stays the same. When I implement an ExceptionMapper for a custom exception and throw the corresponding exception in the resource method though, everything works fine. I assume this has to do with the default ExceptionMapper for JsonProcessingException overriding my own one. Then I tried to create a generic mapper ("implements ExceptionMapper"), but again no success.

I've looked literally everywhere and tried many things including extending ResourceConfig and registering my mapper, but nothing has worked so far.

Some more information that might help to narrow the problem down: I am using Grizzly2 as the HTTP server which I am deploying as a Fat JAR.

The dependency part of my pom.xml looks like this:

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.24</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-grizzly2-http</artifactId>
        <version>2.24</version>
    </dependency>
</dependencies>

Any advice is highly appreciated.

like image 698
vigilanum Avatar asked Nov 07 '16 17:11

vigilanum


2 Answers

Ok, this is dumb and hack-ish, but worked for me:

register(JacksonJaxbJsonProvider.class);

This is due to the following "nice default behavior" in the Jackson feature entry point:

if (!config.isRegistered(JacksonJaxbJsonProvider.class)) { // add the default Jackson exception mappers context.register(JsonParseExceptionMapper.class); context.register(JsonMappingExceptionMapper.class);

:(

But, I'd still prefer an answer that fixes the problem "for real" - ie. without pre-registering components so that the feature cannot configure them properly...

like image 185
BadZen Avatar answered Oct 01 '22 02:10

BadZen


I also faced this issue. If JacksonFeature is registered, you can simply register JacksonJaxbJsonProvider as a workaround.


When the JacksonFeature is in the classpath, it is automatically discovered by Jersey. Another approach to fix it is disabling auto discovery by setting ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE to true. As a result of this, you would need to register other features manually.


Alternatively you can get rid of the jersey-media-json-jackson artifact and use jackson-jaxrs-json-provider instead. With this, you will get rid of JacksonFeature and then you can register your own exception mappers.


One last option and probably what seems to be the correct solution (as pointed in Kysil Ivan's answer) you can write your own exception mapper and then give it a high priority, such as 1. If you use auto discovery, just annotate it with @Provider and @Priority:

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

If you manually register your provider, you can give your provider a binding priority:

@ApplicationPath("/")
public class MyResourceConfig extends ResourceConfig {

    public MyResourceConfig() {
        register(JsonParseExceptionMapper.class, 1);
    }
}

See this answer for more details.

like image 45
cassiomolin Avatar answered Oct 01 '22 02:10

cassiomolin