web-dev-qa-db-fra.com

Comment écrire un gestionnaire d'erreurs global approprié avec Spring MVC / Spring Boot

J'écris une application Web avec Spring 4.0.4 et Spring Boot 1.0.2 utilisant Tomcat comme conteneur Web intégré et je veux implémenter une gestion globale des exceptions qui intercepte toutes les exceptions et les enregistre d'une manière spécifique. Mes exigences simples sont:

  • Je veux gérer globalement toutes les exceptions qui ne sont pas déjà traitées ailleurs (dans un gestionnaire d'exceptions de contrôleur par exemple). Je veux enregistrer le message et je veux afficher un message d'erreur personnalisé à l'utilisateur.
  • Je ne veux pas que Spring ou le conteneur Web enregistre les erreurs par lui-même, car je veux le faire moi-même.

Jusqu'à présent, ma solution ressemble à ceci (simplifié, pas de journalisation et pas de redirection vers une vue d'erreur):

@Controller
@RequestMapping("/errors")
public class ErrorHandler implements EmbeddedServletContainerCustomizer
{
    @Override
    public void customize(final ConfigurableEmbeddedServletContainer factory)
    {
        factory.addErrorPages(new ErrorPage("/errors/unexpected"));
        factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/notfound"));
    }

    @RequestMapping("unexpected")
    @ResponseBody
    public String unexpectedError(final HttpServletRequest request)
    {
        return "Exception: " + request.getAttribute("javax.servlet.error.exception");
    }

    @RequestMapping("notfound")
    @ResponseBody
    public String notFound()
    {
        return "Error 404";
    }
}

Le résultat est que les exceptions levées dans les contrôleurs sont correctement gérées par la méthode unexpectedError et les codes d'état 404 sont gérés par la méthode notFound. Jusqu'ici tout va bien, mais j'ai les problèmes suivants:

  • Tomcat ou Spring (pas sûr de savoir qui est responsable) enregistre toujours le message d'erreur. Je ne veux pas cela parce que je veux l'enregistrer moi-même (avec des informations supplémentaires) et je ne veux pas de messages d'erreur en double dans le journal. Comment puis-je empêcher cette journalisation par défaut?
  • La façon dont j'accède à l'objet d'exception ne semble pas correcte. Je récupère si de l'attribut de demande javax.servlet.error.exception. Et ce n'est même pas l'exception levée, c'est une instance de org.springframework.web.util.NestedServletException et je dois plonger dans cette exception imbriquée pour récupérer la vraie. Je suis sûr qu'il existe un moyen plus simple, mais je ne le trouve pas.

Alors, comment puis-je résoudre ces problèmes? Ou peut-être que la façon dont j'ai implémenté ce gestionnaire d'exceptions global est complètement erronée et qu'il existe une meilleure alternative?

37
kayahr

Jetez un œil à ControllerAdvice Vous pouvez faire quelque chose comme ceci:

@ControllerAdvice
public class ExceptionHandlerController {

    public static final String DEFAULT_ERROR_VIEW = "error";

    @ExceptionHandler(value = {Exception.class, RuntimeException.class})
    public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) {
            ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);

        mav.addObject("datetime", new Date());
        mav.addObject("exception", e);
        mav.addObject("url", request.getRequestURL());
        return mav;
    }
}
55
Michiel