2012-06-28 11 views
23

Mam aplikację Spring MVC, która wykorzystuje FreeMarker jako technologię View (ale może technologia widoku nie ma znaczenia dla mojego pytania). Muszę przechwycić wszystkie wyjątki, które mogą zostać odrzucone podczas żądania.Jak obsłużyć wyjątki zgłaszane podczas renderowania widoku w Spring MVC?

Zaimplementowałem HandlerExceptionResolver, ale ten resolver jest wykonywany tylko wtedy, gdy wyjątek wystąpi w kontrolerze. Ale gdy kontroler zwróci ModelAndView i wystąpi wyjątek podczas renderowania widoku (Ponieważ nie znaleziono zmiennej lub coś podobnego), to nie jest wywoływany wyjątek, a zamiast tego otrzymuję ślad stosu w oknie przeglądarki.

Próbowałem również użyć metody obsługi wyjątku w kontrolerze, która zwraca widok i opatrzono go adnotacją @ExceptionHandler, ale to też nie działa (najprawdopodobniej ponownie, ponieważ wyjątek nie jest generowany w kontrolerze, ale w widoku) .

Czy istnieje mechanizm Spring, w którym można zarejestrować obsługę wyjątków, która przechwytuje błędy wyświetlania?

+0

Czy taka [konfiguracja] (http://developingdeveloper.wordpress.com/2008/03/09/handling-exceptions-in-spring-mvc-part-2/) pomoże? – nobeh

+0

@nobeh Nope, niestety nie. W tym artykule wyjaśniono jedynie użycie rzeczy HandlerExceptionResolver. To już używam, ale przechwytuje tylko wyjątki wyrzucane w kontrolerach, a nie w widokach. – kayahr

Odpowiedz

21

Słowo wstęp: jeśli potrzebujesz tylko "statycznej" strony błędu bez dużej logiki i przygotowania modelu, powinno wystarczyć umieszczenie <error-page> -Tag w twoim web.xml (patrz poniżej przykład).

przeciwnym razie nie mogłyby być lepsze sposoby, aby to zrobić, ale to działa na nas:

Używamy aplet <filter> w web.xml że łapie wszystkie wyjątki i zwraca naszą niestandardową ErrorHandler, to samo używamy wewnątrz sprężyny HandlerExceptionResolver.

<filter> 
    <filter-name>errorHandlerFilter</filter-name> 
    <filter-class>org.example.filter.ErrorHandlerFilter</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>errorHandlerFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 

Realizacja wygląda w zasadzie tak:

public class ErrorHandlerFilter implements Filter { 

    ErrorHandler errorHandler; 

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { 
    try { 
     filterChain.doFilter(request, response); 
    } catch (Exception ex) { 
     // call ErrorHandler and dispatch to error jsp 
     String errorMessage = errorHandler.handle(request, response, ex); 
     request.setAttribute("errorMessage", errorMessage); 
     request.getRequestDispatcher("/WEB-INF/jsp/error/dispatch-error.jsp").forward(request, response); 
    } 

    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
    errorHandler = (ErrorHandler) WebApplicationContextUtils 
     .getRequiredWebApplicationContext(filterConfig.getServletContext()) 
     .getBean("defaultErrorHandler"); 
    } 

    // ... 
} 

Wierzę, że to powinno działać bardzo podobnie do szablonów FreeMarker. Oczywiście, jeśli twój widok błędu zgłasza błąd, masz mniej lub więcej opcji.

Aby również wychwycić błędy jak 404 i przygotować model do niego, używamy filtr, który jest mapowany do ERROR wysyłający:

<filter> 
    <filter-name>errorDispatcherFilter</filter-name> 
    <filter-class>org.example.filter.ErrorDispatcherFilter</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>errorDispatcherFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
    <dispatcher>ERROR</dispatcher> 
</filter-mapping> 

<error-page> 
    <error-code>404</error-code> 
    <location>/WEB-INF/jsp/error/dispatch-error.jsp</location> 
</error-page> 
<error-page> 
    <exception-type>java.lang.Exception</exception-type> 
    <location>/WEB-INF/jsp/error/dispatch-error.jsp</location> 
</error-page> 

doFilter-Realizacja wygląda następująco:

@Override 
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 

    final HttpServletRequest request = (HttpServletRequest) servletRequest; 

    // handle code(s) 
    final int code = (Integer) request.getAttribute("javax.servlet.error.status_code"); 
    if (code == 404) { 
    final String uri = (String) request.getAttribute("javax.servlet.error.request_uri"); 
    request.setAttribute("errorMessage", "The requested page '" + uri + "' could not be found."); 
    } 

    // notify chain 
    filterChain.doFilter(servletRequest, servletResponse); 
} 
+0

Nie użyłem niestandardowej procedury obsługi błędów. Wszystko, co chcę zrobić, to - jeśli przetwarzanie szablonu nie powiedzie się, a następnie przekierować użytkownika do statycznego błędu. Przeglądarka pokazuje zawartość strony błędu, ale pokazuje także ślad stosu. Jakieś wskazówki? Używam Springa. Po prostu serwlety z freemarker do poglądów. – sbidwai

+1

Witam, dziękuję za to, otrzymuję 'org.springframework.beans.factory.NoSuchBeanDefinitionException: Brak komponentu bean o nazwie 'defaultErrorHandler' na init. Czy muszę to zdefiniować w pliku servlet.xml? – Ernest

+0

Czy można to zrobić w przyszłości: 'request.getRequestDispatcher ("/WEB-INF/jsp/error/dispatch-error.jsp ") forward (request, response);' using existing ViewResolver? – FelipeKunzler

0

Nie jestem pewien, czy moje rozwiązanie działa z problemem, który występuje. Chory po prostu pisać tak łapię wyjątki w celu zapewnienia śladu stosu jest pokazanie w przeglądarce:

Zrobiłem klasę AbstractController z metody, która będzie obsługiwać konkretny konflikt takiego:

public class AbstractController { 

    @ResponseStatus(HttpStatus.CONFLICT) 
    @ExceptionHandler({OptimisticLockingFailureException.class}) 
    @ResponseBody 
    public void handleConflict() { 
    //Do something extra if you want 
    } 
} 

ten sposób za każdym razem, gdy wystąpi wyjątek, użytkownik zobaczy domyślny stan HTTPResponse. (np. 404 Not Found itp.)

Rozszerzam tę klasę na wszystkich swoich klasach kontrolerów, aby upewnić się, że błędy są przekierowywane do kontrolera AbstractController. W ten sposób nie muszę używać ExceptionHandler na określonym kontrolerze, ale mogę dodać globalnie do wszystkich kontrolerów. (przez rozszerzenie klasy AbstractController).

Edit: Po kolejnym iść na swoje pytanie, zauważyłem dostajesz błędy w widoku. Nie jestem pewien, czy w ten sposób złapię ten błąd ..

Mam nadzieję, że to pomoże!

+0

Próbowałem już tego. Dodałem taką metodę bezpośrednio do kontrolera, a nie do jakiejś jej klasy bazowej, ale to nie robi różnicy. Ta procedura obsługi wyjątków jest wywoływana, gdy wyjątek jest zgłaszany w ramach metody kontrolera, ale nie w przypadku, gdy wyjątek zostanie zgłoszony w widoku. – kayahr

+0

lub jeśli błąd występuje w filtrze serwletów. Myślę, że zaakceptowana odpowiedź jest jedyną, która obsługuje każdy błąd w dowolnym miejscu. – ticktock

+0

To nie zadziała. Przetwarzanie szablonu odbywa się po tym, jak wywoływane są kontrolery i porady kontrolera. Tak więc jedyny sposób, jaki mogę wymyślić to dodanie linii poleceń do filtrowania zgodnie z sugestią przyjętej odpowiedzi. Nie ładne, ale ma sens. –

6

Można rozszerzyć serwer DispatcherServlet.

W swoim pliku web.xml wymień ogólny DispatcherServlet dla twojej własnej klasy.

<servlet> 
    <servlet-name>springmvc</servlet-name> 
    <servlet-class>com.controller.generic.DispatcherServletHandler</servlet-class> 
    <load-on-startup>1</load-on-startup> 
</servlet> 

Później tworzyć własne klasy DispatcherServletHandler i rozciąga się od DispatcherServlet:

public class DispatcherServletHandler extends DispatcherServlet { 

    private static final String ERROR = "error"; 
    private static final String VIEW_ERROR_PAGE = "/WEB-INF/views/error/view-error.jsp"; 

    @Override 
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 
     try{ 
      super.doService(request, response); 
     } catch(Exception ex) { 
      request.setAttribute(ERROR, ex); 
      request.getRequestDispatcher(VIEW_ERROR_PAGE).forward(request, response); 
     } 
    } 
} 

I w tej stronie mamy tylko pokazać wiadomość do użytkownika.

Powiązane problemy