2014-05-10 16 views
34

Piszę aplikację internetową ze Spring 4.0.4 i Spring Boot 1.0.2 używając Tomcat jako wbudowanego kontenera internetowego i chcę zaimplementować globalną obsługę wyjątków który przechwytuje wszystkie wyjątki i loguje je w określony sposób. Moje wymagania są proste:Jak napisać prawidłową globalną procedurę obsługi błędów za pomocą Spring MVC/Spring Boot

  • Chcę globalnie obsługiwać wszystkie wyjątki, które nie są już zrealizowane gdzie indziej (W obsługi wyjątków kontroler na przykład). Chcę zalogować wiadomość i chcę wyświetlić użytkownikowi komunikat o błędzie niestandardowym.
  • Nie chcę, aby Spring lub kontener sieciowy sam rejestrował błędy, ponieważ sam chcę to zrobić.

Dotychczas moje rozwiązanie wygląda następująco (uproszczony, bez logowania i bez przekierowanie do widoku błędu):

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

Powoduje to, że wyjątki rzucane w sterownikach są prawidłowo obsługiwane przez metodę unexpectedError i 404 kody stanu są obsługiwane przez metodę notFound. Do tej pory tak dobrze, ale mam następujące problemy:

  • Tomcat lub Spring (nie wiem, kto jest odpowiedzialny) nadal rejestruje komunikat o błędzie. Nie chcę tego, ponieważ chcę się zalogować sam (z dodatkowymi informacjami) i nie chcę powtarzać komunikatów o błędach w dzienniku. Jak mogę zapobiec temu domyślnemu rejestrowaniu?
  • Sposób uzyskiwania dostępu do obiektu wyjątku wydaje się nieprawidłowy. Pobieram, jeśli z atrybutu żądania javax.servlet.error.exception. I to nie jest nawet wyjątek odrzucony, jest to instancja org.springframework.web.util.NestedServletException i muszę zagłębić się w ten wyjątek zagnieżdżony, aby pobrać prawdziwy. Jestem prawie pewien, że jest łatwiejszy sposób, ale nie mogę go znaleźć.

Jak mogę rozwiązać te problemy? A może sposób, w jaki zaimplementowałem ten globalny program obsługi wyjątków jest całkowicie błędny i istnieje lepsza alternatywa?

Odpowiedz

51

Wystarczy popatrzeć na ControllerAdvice Można zrobić coś takiego:

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

Dzięki, to działa świetnie. Ale teraz odkryłem inny wymóg, który nie jest częścią mojego pierwotnego pytania, więc stworzyłem nowy. Być może możesz tam także pomóc: http://stackoverflow.com/questions/23582534/how-to-handle-exceptions-in-spring-mvc-differently-for-html-and-json-requests – kayahr

+1

Najczystsze dostępne rozwiązanie. Innym godnym dodatkiem może być @ResponseStatus do 500, który zwróci odpowiedź w HTTP 500 zgodnie z oczekiwaniami. –

+3

Należy pamiętać, że to rozwiązanie spowoduje również wychwycenie wyjątków Spring Security, takich jak AuthenticationException. W rezultacie przekierowanie do chronionego zasobu po zalogowaniu nie będzie działać. Objaśnienie: wprowadzenie chronionego zasobu zwykle przekierowuje do strony logowania. Po zalogowaniu powinieneś wrócić do chronionego zasobu (Spring Security dba o to, przechwytując AuthenticationException), ale kiedy złapiesz wszystkie wyjątki, Spring Security już go nie złapie. – Kacper86

Powiązane problemy