Spring @ExceptionHandler no funciona con @ResponseBody

Intento configurar un manejador de excepción de spring para un controlador de reposo que pueda representar un mapa tanto para xml como para json en función del encabezado de recepción entrante. Lanza una excepción de 500 servlets en este momento.

Esto funciona, recoge el home.jsp:

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

Esto no funciona:

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

En el mismo mapeo de controlador funciona la respuesta a xml o json a través del convertidor respectivo:

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

¿Nadie?

Tu método

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

no funciona porque tiene el tipo de devolución incorrecto. Los métodos @ExceptionHandler tienen solo dos tipos de devolución válidos:

  • Cuerda
  • ModelAndView.

Consulte http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html para obtener más información. Aquí está el texto específico del enlace:

El tipo de devolución puede ser una Cadena, que se interpreta como un nombre de vista o un objeto ModelAndView.

En respuesta al comentario

Gracias, parece que he leído demasiado esto. Eso es malo … ¿Alguna idea de cómo se proporcionan excepciones automáticamente en formato xml / json? – Sven Haiges hace 7 horas

Esto es lo que hice (en realidad lo hice en Scala, así que no estoy seguro de si la syntax es exactamente la correcta, pero debes obtener la esencia).

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

Gracias, parece que he leído demasiado esto. Eso es malo … ¿Alguna idea de cómo se proporcionan excepciones automáticamente en formato xml / json?

Nuevo en Spring 3.0 MappingJacksonJsonView se puede utilizar para lograr eso:

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

Esto parece un error confirmado (SPR-6902 @ResponseBody no funciona con @ExceptionHandler)

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

Solucionado en 3.1 M1 aunque …

Estoy usando Spring 3.2.4. Mi solución al problema fue asegurarme de que el objeto que regresaba del manejador de excepciones tuviera getters.

Sin getters, Jackson no pudo serializar el objeto a JSON.

En mi código, para el siguiente ExceptionHandler:

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

Necesitaba asegurarme de que mi objeto ErrorInfo tuviera 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(); } } 

Lo siguiente podría ser una solución si está utilizando convertidores de mensajes para ordenar objetos de error como el contenido de la respuesta

 @ExceptionHandler(IllegalArgumentException.class) public String handleException(final Exception e, final HttpServletRequest request) { final Map map = new HashMap(); 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 showError(HttpServletRequest request, HttpServletResponse response){ Map map = new HashMap(); if(request.getAttribute("error") != null) map = (Map) request.getAttribute("error"); response.setStatus(Integer.parseInt(map.get("errorCode").toString())); return map; } 

AnnotationMethodHandlerExceptionResolver también necesita MappingJacksonHttpMessageConverter

           

Enfrenté el problema similar, este problema ocurre cuando el tipo de devolución del método del controlador y los tipos de devolución de ExceptionHandler no son los mismos . Asegúrese de tener exactamente los mismos tipos de devolución.

Método del controlador:

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

Método de asesoramiento:

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

Como puede ver, los tipos de devolución en ambas clases son los mismos ResponseEntity< ?> .