Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Spring MVC, accepting POST requests with bad JSON leads to a default 400 error code server page being returned

I am working on a REST api. Receiving a POST message with bad JSON (e.g. { sdfasdfasdf } ) causes Spring to return the default server page for a 400 Bad Request Error. I do not want to return a page, I want to return a custom JSON Error object.

I can do this when there is an exception thrown by using an @ExceptionHandler. So if it is a blank request or a blank JSON object (e.g. { } ), it will throw a NullPointerException and I can catch it with my ExceptionHandler and do whatever I please.

The problem then, is that Spring doesn't actually throw an exception when it is just invalid syntax... at least not that I can see. It simply returns the default error page from the server, whether it is Tomcat, Glassfish, etc.

So my question is how can I "intercept" Spring and cause it to use my exception handler or otherwise prevent the error page from displaying and instead return a JSON error object?

Here is my code:

@RequestMapping(value = "/trackingNumbers", method = RequestMethod.POST, consumes = "application/json")
@ResponseBody
public ResponseEntity<String> setTrackingNumber(@RequestBody TrackingNumber trackingNumber) {

    HttpStatus status = null;
    ResponseStatus responseStatus = null;
    String result = null;
    ObjectMapper mapper = new ObjectMapper();

    trackingNumbersService.setTrackingNumber(trackingNumber);
    status = HttpStatus.CREATED;
    result = trackingNumber.getCompany();


    ResponseEntity<String> response = new ResponseEntity<String>(result, status);

    return response;    
}

@ExceptionHandler({NullPointerException.class, EOFException.class})
@ResponseBody
public ResponseEntity<String> resolveException()
{
    HttpStatus status = null;
    ResponseStatus responseStatus = null;
    String result = null;
    ObjectMapper mapper = new ObjectMapper();

    responseStatus = new ResponseStatus("400", "That is not a valid form for a TrackingNumber object " + 
            "({\"company\":\"EXAMPLE\",\"pro_bill_id\":\"EXAMPLE123\",\"tracking_num\":\"EXAMPLE123\"})");
    status = HttpStatus.BAD_REQUEST;

    try {
        result = mapper.writeValueAsString(responseStatus);
    } catch (IOException e1) {
        e1.printStackTrace();
    }

    ResponseEntity<String> response = new ResponseEntity<String>(result, status);

    return response;
}
like image 336
UpAllNight Avatar asked Jun 19 '13 20:06

UpAllNight


People also ask

How to handle exception in rest api using Spring Boot?

Altogether, the most common way is to use @ExceptionHandler on methods of @ControllerAdvice classes so that the exception handling will be applied globally or to a subset of controllers. ControllerAdvice is an annotation introduced in Spring 3.2, and as the name suggests, is “Advice” for multiple controllers.

How to handle exceptions in rest api?

To deal with exceptions, the recommended practice is to follow the sequence outlined below: Determine whether the REST API request succeeded or failed, based on the HTTP status response code returned. If the REST API request failed and the response is application/json, serialize the model.

How do I return error in ResponseEntity?

Change your return type to a ResponseEntity<Object> or to a ResponseEntity<?> . Instead of passing a null argument for your error case, pass in a String . Spring is smart enough to see the String and write it as text to the response body.


1 Answers

This was raised as an issue with Spring SPR-7439 - JSON (jackson) @RequestBody marshalling throws awkward exception - which was fixed in Spring 3.1M2 by having Spring throw a org.springframework.http.converter.HttpMessageNotReadableException in the case of a missing or invalid message body.

In your code you cannot create a ResponseStatus since it is abstract but I tested catching this exception with a simpler method locally with Spring 3.2.0.RELEASE running on Jetty 9.0.3.v20130506.

@ExceptionHandler({org.springframework.http.converter.HttpMessageNotReadableException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public String resolveException() {
    return "error";
}

and I received a 400 status "error" String response.

The defect was discussed on this Spring forum post.

Note: I started testing with Jetty 9.0.0.M4 but that had some other internal issues stopping the @ExceptionHandler completing, so depending on your container (Jetty, Tomcat, other) version you might need to get a newer version that plays nicely with whatever version of Spring you are using.

like image 161
andyb Avatar answered Sep 23 '22 12:09

andyb