Spring @ExceptionHandler не работает с @ResponseBody
Я пытаюсь настроить обработчик исключений Spring для контроллера rest, который может отображать карту как в 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");
}
Кто-нибудь?
7 ответов
Ваш метод
@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 для получения дополнительной информации. Вот конкретный текст по ссылке:
Тип возвращаемого значения может быть String, который интерпретируется как имя представления или объект ModelAndView.
В ответ на комментарий
Спасибо, кажется, я переоценил это. Это плохо... есть идеи, как автоматически создавать исключения в формате xml / json? - Свен Хейгес 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()));
}
Спасибо, кажется, я переоценил это. Это плохо... есть идеи, как автоматически создавать исключения в формате 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 ) );
}
Это похоже на подтвержденную ошибку (SPR-6902 @ResponseBody не работает с @ExceptionHandler)
https://jira.springsource.org/browse/SPR-6902
Исправлено в 3.1 M1, хотя...
Я использую Spring 3.2.4. Мое решение проблемы состояло в том, чтобы убедиться, что объект, который я возвращал из обработчика исключений, имел геттеры.
Без получателей Джексон не смог сериализовать объект в JSON.
В моем коде, для следующего ExceptionHandler:
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public List<ErrorInfo> exceptionHandler(Exception exception){
return ((ConversionException) exception).getErrorInfos();
}
Мне нужно было убедиться, что у моего объекта ErrorInfo есть геттеры:
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();
}
}
Следующее может быть обходным путем, если вы используете конвертеры сообщений для объектов ошибок маршала в качестве содержимого ответа
@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;
}
Аннотация MethodHandlerExceptionResolver также требуется 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" />
Я сталкивался с подобной проблемой, эта проблема возникает, когда ваш тип возврата метода 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<?>
,