wake-up-neo.com

So schreiben Sie einen richtigen globalen Fehlerbehandler mit Spring MVC / Spring Boot

Ich schreibe eine Webanwendung mit Spring 4.0.4 und Spring Boot 1.0.2 unter Verwendung von Tomcat als eingebettetem Webcontainer und möchte eine globale Ausnahmebehandlung implementieren, die alle Ausnahmen abfängt und sie auf eine bestimmte Weise protokolliert. Meine einfachen Anforderungen sind:

  • Ich möchte alle Ausnahmen global behandeln, die noch nicht an einem anderen Ort verarbeitet wurden (z. B. in einem Controller-Ausnahmebehandler). Ich möchte die Nachricht protokollieren und dem Benutzer eine benutzerdefinierte Fehlermeldung anzeigen.
  • Ich möchte nicht, dass Spring oder der Webcontainer selbst Fehler protokollieren, da ich dies selbst tun möchte.

Bisher sieht meine Lösung so aus (vereinfacht, keine Protokollierung und keine Umleitung zu einer Fehleransicht):

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

Das Ergebnis ist, dass in Controllern ausgelöste Ausnahmen korrekt von der unexpectedError -Methode und 404-Statuscodes von der notFound -Methode behandelt werden. Soweit so gut, aber ich habe folgende Probleme:

  • Tomcat oder Spring (nicht sicher, wer dafür verantwortlich ist) protokollieren die Fehlermeldung weiterhin. Ich möchte das nicht, weil ich es selbst (mit zusätzlichen Informationen) protokollieren möchte und keine doppelten Fehlermeldungen im Protokoll haben möchte. Wie kann ich diese Standardprotokollierung verhindern?
  • Die Art und Weise, wie ich auf das Ausnahmeobjekt zugreife, scheint nicht richtig zu sein. Ich hole ob aus dem Anforderungsattribut javax.servlet.error.exception. Und das ist nicht einmal die geworfene Ausnahme, es ist eine Instanz von org.springframework.web.util.NestedServletException und ich muss in diese verschachtelte Ausnahme eintauchen, um die echte zu holen. Ich bin mir ziemlich sicher, dass es einen einfacheren Weg gibt, aber ich kann ihn nicht finden.

Wie kann ich diese Probleme lösen? Oder ist die Art und Weise, wie ich diesen globalen Ausnahmebehandler implementiert habe, völlig falsch und es gibt eine bessere Alternative?

37
kayahr

Schauen Sie sich ControllerAdvice an. Sie könnten so etwas tun:

@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