Подтвердить что ты не робот

Spring @ExceptionHandler не работает с @ResponseBody

Я пытаюсь настроить обработчик исключений spring для контроллера покоя, который может отображать карту как для xml, так и для json на основе заголовка принимающего accept. Сейчас он выбрасывает исключение 500 сервлетов.

Это работает, он поднимает home.jsp:

@ExceptionHandler(IllegalArgumentException.class)
public String handleException(final Exception e, final HttpServletRequest request, Writer writer)
{
    return "home";
}

Это не работает:

@ExceptionHandler(IllegalArgumentException.class)
public @ResponseBody Map<String, Object> handleException(final Exception e, final HttpServletRequest request, Writer writer)
{
    final Map<String, Object> map = new HashMap<String, Object>();
    map.put("errorCode", 1234);
    map.put("errorMessage", "Some error message");
    return map;
}

В том же контрольном сопоставлении работает ответ на xml или json через соответствующий преобразователь:

@RequestMapping(method = RequestMethod.GET, value = "/book/{id}", headers = "Accept=application/json,application/xml")
public @ResponseBody
Book getBook(@PathVariable final String id)
{
    logger.warn("id=" + id);
    return new Book("12345", new Date(), "Sven Haiges");
}

Кто-нибудь?

4b9b3361

Ответ 1

Ваш метод

@ExceptionHandler(IllegalArgumentException.class)
public @ResponseBody Map<String, Object> handleException(final Exception e, final HttpServletRequest request, Writer writer)

не работает, потому что у него неправильный тип возврата. Методы @ExceptionHandler имеют только два допустимых типа возврата:

  • Строка
  • ModelAndView.

Подробнее см. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html. Вот конкретный текст по ссылке:

Тип возврата может быть строкой, которая интерпретируется как имя вида или Объект ModelAndView.

В ответ на комментарий

Thanx, кажется, я это перепробовал. Это плохой... любые идеи, как обеспечить исключений автоматически в xml/json формат? - Sven Haiges 7 часов назад

Вот что я сделал (я действительно сделал это в Scala, поэтому я не уверен, что синтаксис точно верен, но вы должны получить суть).

@ExceptionHandler(Throwable.class)
@ResponseBody
public void handleException(final Exception e, final HttpServletRequest request,
        Writer writer)
{
    writer.write(String.format(
            "{\"error\":{\"java.class\":\"%s\", \"message\":\"%s\"}}",
            e.getClass(), e.getMessage()));
}

Ответ 2

Thanx, кажется, я это перепробовал. Это плохо... любые идеи, как обеспечить исключений автоматически в формате xml/json?

Новое в Spring 3.0 MappingJacksonJsonView может быть использовано для достижения этого:

private MappingJacksonJsonView  jsonView = new MappingJacksonJsonView();

@ExceptionHandler(Exception.class)
public ModelAndView handleAnyException( Exception ex )
{
    return new ModelAndView( jsonView, "error", new ErrorMessage( ex ) );
}

Ответ 3

Это похоже на подтвержденный Bug (SPR-6902 @ResponseBody не работает с @ExceptionHandler)

https://jira.springsource.org/browse/SPR-6902

Исправлено в 3.1 M1, хотя...

Ответ 4

Я использую Spring 3.2.4. Мое решение проблемы состояло в том, чтобы убедиться, что объект, который я возвращал из обработчика исключений, имел getters.

Без получателей Джексон не смог сериализовать объект в JSON.

В моем коде для следующего ExceptionHandler:

@ExceptionHandler(RuntimeException.class)
@ResponseBody
public List<ErrorInfo> exceptionHandler(Exception exception){
    return ((ConversionException) exception).getErrorInfos();
}

Мне нужно было убедиться, что у моего объекта ErrorInfo есть getters:

package com.pelletier.valuelist.exception;

public class ErrorInfo {
private int code;
private String field;
private RuntimeException exception;

public ErrorInfo(){}

public ErrorInfo(int code, String field, RuntimeException exception){
    this.code = code;
    this.field = field;
    this.exception = exception;
}

public int getCode() {
    return code;
}

public String getField() {
    return field;
}

public String getException() {
    return exception.getMessage();
}
}

Ответ 5

Следующее может быть обходным путем, если вы используете преобразователи сообщений для маршаллинга объектов ошибки в качестве содержимого ответа

@ExceptionHandler(IllegalArgumentException.class)
public String handleException(final Exception e, final HttpServletRequest request)
{
    final Map<String, Object> map = new HashMap<String, Object>();
    map.put("errorCode", 1234);
    map.put("errorMessage", "Some error message");
    request.setAttribute("error", map);
    return "forward:/book/errors"; //forward to url for generic errors
}

//set the response status and return the error object to be marshalled
@SuppressWarnings("unchecked")
@RequestMapping(value = {"/book/errors"}, method = {RequestMethod.POST, RequestMethod.GET})
public @ResponseBody Map<String, Object> showError(HttpServletRequest request, HttpServletResponse response){

    Map<String, Object> map = new HashMap<String, Object>();
    if(request.getAttribute("error") != null)
        map = (Map<String, Object>) request.getAttribute("error");

    response.setStatus(Integer.parseInt(map.get("errorCode").toString()));

    return map;
}

Ответ 6

AnnotationMethodHandlerExceptionResolver также нуждается в MappingJacksonHttpMessageConverter

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
    <property name="messageConverters">
        <list>
            <bean
                class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="jacksonObjectMapper" />
            </bean>
        </list>
    </property>
</bean>

<bean id="jacksonObjectMapper"
    class="iacm.cemetery.framework.web.servlet.rest.JacksonObjectMapper" />

Ответ 7

Я столкнулся с подобной проблемой, эта проблема возникает, когда возвращаемый тип метода Controller и типы возврата ExceptionHandler не являются одинаковыми. Убедитесь, что у вас одинаковые типы возврата.

Метод контроллера:

@RequestMapping(value = "/{id}", produces = "application/json", method = RequestMethod.POST)
public ResponseEntity<?> getUserById(@PathVariable String id) throws NotFoundException {
    String response = userService.getUser(id);
    return new ResponseEntity(response, HttpStatus.OK);
}

Метод консультаций:

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<?> notFoundException(HttpServletRequest request, NotFoundException e) {
    ExceptionResponse response = new ExceptionResponse();
    response.setSuccess(false);
    response.setMessage(e.getMessage());
    return new ResponseEntity(response, HttpStatus.NOT_FOUND);
}

Как вы можете видеть, типы возврата в обоих классах одинаковы ResponseEntity<?>.