Mono Jamoon
Mono Jamoon

Reputation: 4607

How to handle parser exceptions during unmarshalling of JSON data?

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.

Upvotes: 8

Views: 12774

Answers (6)

Mordechai
Mordechai

Reputation: 16264

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.

Upvotes: 1

user
user

Reputation: 1

Add the exception handling class in same package(or sub package) mentioned in the web.xml file

Upvotes: 0

Dattatray
Dattatray

Reputation: 1871

The other option could be in the ResourceConfig, register the JsonParseExceptionMapper class.

register(JsonParseExceptionMapper.class);

Upvotes: 0

max
max

Reputation: 501

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.

Upvotes: 2

Batakj
Batakj

Reputation: 12743

Adding full class example of Perception answer

package com.shashi.service;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import org.apache.log4j.Logger;

import org.codehaus.jackson.JsonParseException;
import com.sun.jersey.api.client.ClientResponse.Status;
import com.talklingo.service.v1.UserManagementService;
@Provider
public class JSONParseExceptionMapper implements ExceptionMapper< JsonParseException > {

 private static Logger logger = Logger
   .getLogger(JSONParseExceptionMapper.class);

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

     }
}

Upvotes: 0

Perception
Perception

Reputation: 80603

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

Upvotes: 17

Related Questions