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 instancjaorg.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?
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
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. –
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