Используя Spring MVC, прием POST-запросов с плохим JSON приводит к возврату страницы сервера ошибок по умолчанию 400

Я работаю над REST api. Получение сообщения POST с плохим JSON (например, {sdfasdfasdf}) приводит к тому, что Spring возвращает страницу сервера по умолчанию для ошибки 400 Bad Request. Я не хочу возвращать страницу, я хочу вернуть пользовательский объект JSON Error.

Я могу сделать это, когда есть исключение, созданное с помощью @ExceptionHandler. Поэтому, если это пустой запрос или пустой объект JSON (например, {}), он выкинет исключение NullPointerException, и я поймаю его с помощью своего ExceptionHandler и сделаю все, что захочу.

Проблема заключается в том, что Spring фактически не генерирует исключение, когда это просто недопустимый синтаксис... по крайней мере, я не вижу этого. Он просто возвращает страницу с ошибкой по умолчанию с сервера, будь то Tomcat, Glassfish и т.д.

Итак, мой вопрос: как я могу "перехватить" Spring и заставить его использовать мой обработчик исключений или иным образом предотвратить отображение страницы ошибки и вместо этого вернуть объект ошибки JSON?

Вот мой код:

@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;
}

Ответ 1

Это было поднято как проблема с Spring SPR-7439 - JSON (jackson) @RequestBody marshalling бросает неудобное исключение - которое было исправлено в Spring 3.1M2, имея Spring бросить a org.springframework.http.converter.HttpMessageNotReadableException в случае отсутствия или недопустимого тела сообщения.

В вашем коде вы не можете создать ResponseStatus, так как он абстрактный, но я тестировал это исключение с помощью более простого метода локально с Spring 3.2.0.RELEASE, запущенным на Jetty 9.0.3.v20130506.

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

и я получил сообщение об ошибке "ошибка строки" 400.

Дефект обсуждался на этом Spring форуме.

Примечание.. Я начал тестировать Jetty 9.0.0.M4, но у меня были другие внутренние проблемы, останавливающие завершение @ExceptionHandler, поэтому в зависимости от вашей версии (Jetty, Tomcat, other) вы может потребоваться получить более новую версию, которая будет хорошо работать с любой версией Spring, которую вы используете.