24

I'm using Jersey's integrated Jackson processing to transform incoming JSON to a POJO, e.g.:

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response newCustomer( CustomerRepresentation customer)
{
...
}

If a client sends JSON with invalid fields Jersey currently returns a 500 Internal Server Error. Instead, I'd like to return a 400 Bad Request, preferably with some meaningful detail indicating which fields are in error.

Any insight into how this could be accomplished? (At least returning a generic 400 instead of the completely inappropriate 500?)

Update: Here's the exception being generated server-side, before my handler is invoked:

javax.servlet.ServletException: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: 
Unrecognized field "this_isnt_a_known"_field" (Class com.redacted....), not marked as ignorable
HolySamosa
  • 9,011
  • 14
  • 69
  • 102
  • `400` actually means `Bad request`, i.e. the server thinks that client has send the wrong request. So semantically it does not fit what you want to achieve. – dma_k Mar 25 '12 at 15:28
  • Thanks for catching my slip. I intended to type "Bad Request". Question edited accordingly. – HolySamosa Mar 26 '12 at 13:05
  • Jersey usually returns 400 if the request body is invalid. Are you sure it doesn't fail in the body of your method with a NPE or something? Do you see any error and/or stack trace in your app server logs? – TheArchitect Mar 26 '12 at 17:15
  • Thanks! It's good to know that Jersey is *supposed* to return 400. I'm updating the question to include the server-side exception. It's definitely being generated is being generated before reaching my handler and is related to an unrecognized field in the JSON. – HolySamosa Mar 27 '12 at 17:06
  • @HolySamosa: What you can do is to run your web container in debug mode, put breakpoint when `UnrecognizedPropertyException` is raised and see what happens when it is processed. Perhaps you'll catch a bug in Jersey. – dma_k Mar 28 '12 at 08:13

4 Answers4

25

I was finally able to work around this problem by implementing an ExceptionMapper to catch the UnrecognizedPropertyException thrown by Jackson and map it to a 400 Bad Request response:

@Provider
public class UnrecognizedPropertyExceptionMapper implements ExceptionMapper<UnrecognizedPropertyException>
{

    @Override
    public Response toResponse(UnrecognizedPropertyException exception)
    {
        return Response
                .status(Response.Status.BAD_REQUEST)
                .entity( "'" + exception.getUnrecognizedPropertyName() + "' is an unrecognized field.")
                .type( MediaType.TEXT_PLAIN)
                .build();
    }

}
HolySamosa
  • 9,011
  • 14
  • 69
  • 102
  • @HolySamosa Thanks for sharing this. but somehow `toResponse()` is not getting invoked. – Aman Gupta Jul 08 '14 at 11:07
  • 1
    hey HolySamosa...Can you let us know how is your toResponse() is invoked. I am stuck with this issue and did not have any solution to throw 400 instead of 500 error. – ankit Mar 01 '16 at 08:42
  • +1 Because your solution gave me the a tip for troubleshooting. I got the **HTTP 400 Bad request** error with invalid data type in a "multipart/form-data". Catch a general exception helped me to find the issue -> public class ExceptionInterceptor implements ExceptionMapper – bitfox Jul 25 '20 at 16:47
3

I tried mapping status 500 to status 400 with HolySamosa's answer but the exception was not caught by this mapper, and status 500 was still being returned.

After debugging I found that JsonParseException is being thrown and not UnrecognizedPropertyException. This is because I was sending some garbage text (that was not JSON at all).

When I sent a proper JSON from client side, with format that was not appropriate for my DTO on the server side, then I got UnrecognizedPropertyException. So there are two cases for this:

  • when you send garbage that is not JSON and
  • when you send JSON, but it is not a match for your DTO class.

Now I am returning status 400 for both.

jovankricka
  • 279
  • 3
  • 8
3

In dropwizard land there is an ExceptionMapper called JsonProcessingExceptionMapper that has similar functionality as to what you are looking for. Maybe you can use that for inspiration on how to address your specific issue in a non-dropwizard world.

Trevor Mack
  • 155
  • 3
  • 8
1

I've had this same problem... Unfortunately, there's no good way that I know of to intercept the Jackson exception and generate your own error code.

One option you have is to use @JsonIgnoreProperties and then strictly validate the deserialized object. This won't tell you if your sender transmitted junk, but if they missed required fields, you'll catch that.

I cannot find any way to access the actual JSON passed in, other than creating an @Provider class to trap the JSON, validate it, then pass it to Jackson for deserialization.

JasonB
  • 302
  • 1
  • 3
  • 13
  • Jersey can pass the JSON to your handler as a JsonNode which would allow us to validate well formed JSON, but Jersey would still return 500 for a non-JSON request (or so I assume-- I haven't tried yet). – HolySamosa Mar 28 '12 at 19:45
  • 1
    I finally returned to working on this issue and was able to work around it using an ExceptionMapper. – HolySamosa May 24 '12 at 13:08