Spring @ExceptionHandler ne fonctionne pas avec @responses

j'essaie de configurer un gestionnaire d'exception de ressort pour un contrôleur rest qui est capable de rendre une table à la fois xml et json basée sur l'en-tête Accept entrant. Il lance une exception de 500 servlets en ce moment.

Cela fonctionne, il reprend la maison.jsp:

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

Cela ne fonctionne pas:

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

dans le même contrôleur mappant la réponse à xml ou json via le convertisseur respectif fonctionne:

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

tout le monde?

26
demandé sur Sven Haiges 2011-02-24 00:08:41

7 réponses

votre méthode

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

ne fonctionne pas car il a le mauvais type de retour. Les méthodes @ ExceptionHandler n'ont que deux types de retour valides:

  • Chaîne
  • ModelAndView.

Voir http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html pour plus d'informations. Voici le texte spécifique à partir du lien:

le type de retour peut être une chaîne, qui être interprété comme un nom de vue ou un Objet ModelAndView.

En réponse au commentaire

Thanx, il semble que j'ai exagéré. C'est mauvais... toutes les idées comment fournir exceptions automatiquement dans xml / json format? - Sven Haiges il y a 7 heures

voici ce que j'ai fait (je l'ai fait en Scala donc je ne suis pas sûr si la syntaxe est exactement correcte, mais vous devriez obtenir l'essentiel).

@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()));
}
19
répondu three-cups 2011-02-24 15:49:53

Thanx, il semble que j'ai exagéré. C'est un mauvais... toutes les idées comment fournir exceptions automatiquement au format xml/json?

nouveau au printemps 3.0 MappingJacksonJsonView peut être utilisé pour atteindre cet objectif:

private MappingJacksonJsonView  jsonView = new MappingJacksonJsonView();

@ExceptionHandler(Exception.class)
public ModelAndView handleAnyException( Exception ex )
{
    return new ModelAndView( jsonView, "error", new ErrorMessage( ex ) );
}
13
répondu Mikhail Skotnikov 2011-11-08 08:33:01

cela semble être un Bug confirmé (SPR-6902 @Responsabilebody ne fonctionne pas avec @ExceptionHandler)

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

fixé à 3.1 M1 cependant...

10
répondu Eran Medan 2011-05-06 05:44:53

J'utilise le ressort 3.2.4. Ma solution au problème était de m'assurer que l'objet que je retournais de l'exception handler avait getters.

sans getters, Jackson n'a pas pu sérialiser L'objet vers JSON.

Dans mon code, pour la suite de ExceptionHandler:

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

je devais m'assurer que mon objet ErrorInfo avait des 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();
}
}
6
répondu ryanp102694 2016-06-16 22:56:25

ce qui suit pourrait être une solution de contournement si vous utilisez des convertisseurs de messages pour marshall error objects comme le contenu de la réponse

@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;
}
5
répondu astronauthere 2011-08-15 11:21:31

AnnotationMethodHandlerExceptionresolver a également besoin de 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" />
3
répondu wilson 2013-03-21 06:42:41

j'ai affronté le MÊME PROBLÈME, Ce problème se produit lorsque votre type de retour de méthode Controller et les types de retour ExceptionHandler ne sont pas . Assurez-vous que vous avez exactement les mêmes types de retour.

méthode du contrôleur:

@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éthode du Conseil:

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

Comme vous pouvez le voir types de retour dans les deux classes sont les mêmes ResponseEntity<?>.

0
répondu Ankur Mahajan 2016-12-01 12:10:31