How to handle parser exceptions during unmarshalling of JSON data?

12,126

Solution 1

Register an exception mapper to handle the JSON parsing exceptions:

@Provider
class JSONParseExceptionMapper implements ExceptionMapper< JsonParseException > {
    @Override
    public Response toResponse(final JsonParseException jpe) {
        // Create and return an appropriate response here
        return Response.status(Status.BAD_REQUEST)
                .entity("Invalid data supplied for request").build();
    }
}

Solution 2

If this exception mapper isn't being called (as John Ding's comment indicated), make sure you have it registered with your ResourceConfig.

Also, take note that when using the JacksonFeature (with Jersey 2.x) it includes an exception mapper for this, but if you register your own it will take precedence.

Here is an example of registering it by package. So put JSONParseExceptionMapper in "your.package.here" and it will get picked up.

@ApplicationPath("whatever")
public class MyAppResourceConfig extends ResourceConfig
{
    public MyAppResourceConfig()
    {
        packages("your.package.here");
        register(JacksonFeature.class);
    }
}

For the original question, you'll need to handle JsonMappingExceptionMapper as well.

Solution 3

In addition to @Perception's answer, the missing part is that the exceptions get swallowed and not reported. You should pass a validation listener:

private static final String[] severity = new String[] {"Warning", "Error", "Fatal Error"};
void beforeUnmarshal(Unmarshaller u, Object parent) throws JAXBException {
    u.setEventHandler((evt) -> {
        throw new WebApplicationException(severity[evt.getSeverity()]+": Error while parsing request body: " + evt.getMessage(), evt.getLinkedException(), 400);
    });
}

Just drop this code in your JAXB bean and your all set.

Share:
12,126
Mono Jamoon
Author by

Mono Jamoon

Updated on June 12, 2022

Comments

  • Mono Jamoon
    Mono Jamoon almost 2 years

    I am using Jersey in my Web-application. The data sent to the server is in JSON format, which in turn is unmarshalled at the server-end and the object obtained is used in further processing. The security audit raised some vulnerabilities for this approach.

    My Rest Code:

    @POST
    @Path("/registerManga")
    @Produces(MediaType.APPLICATION_JSON)
    public Response registerManga(MangaBean mBean){
        System.out.println(mBean);
        return Response.status(200).build();
    }
    

    MangaBean:

    public class MangaBean {
        public String title;
        public String author;
    
        @Override
        public String toString() {
            return "MangaBean [title=" + title + ", author=" + author + "]";
        }
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }
        public String getAuthor() {
            return author;
        }
        public void setAuthor(String author) {
            this.author = author;
        }
    
    
    }
    

    The data is sent in this format:

    ["title":"Bleach","author":"kubo tite"]
    

    The above data is successfully unmarshalled into an object and I get this as the output:

    MangaBean [title=Bleach, author=kubo tite]
    

    But if the data is changed to:

    ["title":"<script>alert("123");</script>","author":"kubo tite"]
    

    A 500 internal server error occurs and is displayed to the user:

    javax.servlet.ServletException: org.codehaus.jackson.JsonParseException: Unexpected character ('1' (code 49)): was expecting comma to separate OBJECT entries
     at [Source: org.apache.catalina.connector.CoyoteInputStream@19bd1ca; line: 1, column: 28]
        com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:420)
        com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
        com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:699)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    

    The unexpected occurrence of "" is causing errors in the parser. As the unmarshalling is done behind the scenes and I have no control over it, I am unable to handle the exception being raised.

    My question is how can I handle this exception and return a proper response to the user instead of a stacktrace. Please advice.