Jak obsłużyć wyjątki rzucane podczas renderowania widoku w Spring MVC?

Mam aplikację Spring MVC, która używa Freemarkera jako technologii widoku (ale może technologia widoku nie ma znaczenia dla mojego pytania). Muszę przechwycić wszystkie wyjątki, które mogą zostać rzucone podczas żądania.

Zaimplementowałem HandlerExceptionResolver Ale To rozwiązanie jest wykonywane tylko wtedy, gdy wyjątek występuje w kontrolerze. Ale gdy kontroler zwraca ModelAndView i wystąpi wyjątek podczas renderowania widoku (ponieważ zmienna była not found lub coś w tym stylu) wtedy nie jest wywoływany Exception resolver i zamiast tego dostaję ślad stosu w oknie przeglądarki.

Próbowałem również użyć metody obsługi wyjątków wewnątrz kontrolera, która zwraca widok i dodała do niego adnotację @ExceptionHandler, ale to również nie działa (najprawdopodobniej ponownie, ponieważ wyjątek nie jest wrzucany do kontrolera, ale do widoku).

Jest więc jakiś mechanizm sprężynowy, w którym mogę zarejestrować obsługę wyjątków, która przechwytuje widok błędy?

Author: kayahr, 2012-06-28

3 answers

Słowo z góry: jeśli po prostu potrzebujesz "statycznej" strony błędu bez większej logiki i przygotowania modelu, wystarczy umieścić <error-page>-Tag w web.xml (Zobacz poniżej przykład).

W przeciwnym razie, mogą być lepsze sposoby, aby to zrobić, ale to działa dla nas:

Używamy servleta <filter> w web.xml, który wychwytuje wszystkie wyjątki i wywołuje nasz Niestandardowy ErrorHandler, tego samego, którego używamy wewnątrz Spring 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>

Implementacja wygląda zasadniczo 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");
  }

  // ...
}

Uważam, że powinno to działać podobnie w przypadku szablonów FreeMarker. Oczywiście, jeśli twój widok błędu wyświetli błąd, mniej więcej nie masz opcji.

Aby również wyłapać błędy takie jak 404 i przygotować model do niego, używamy filtra, który jest mapowany do ERROR dispatcher:

<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>

Implementacja doFilter wygląda tak:

@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);
}
 21
Author: oxc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-28 15:08:59

Możesz rozszerzyć DispatcherServlet.

W sieci.XML zastąp generic DispatcherServlet dla 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 utwórz własną klasę DispatcherServletHandler i rozszerza ją z 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 na tej stronie musimy tylko pokazać wiadomość do użytkownika.

 6
Author: cralfaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-20 18:52:29

Nie wiem, czy moje rozwiązanie pasuje do twojego problemu. Ill just post the way I catch my exceptions to ensure no stack trace is show inside the browser:

Stworzyłem klasę AbstractController z metodą, która będzie obsługiwać określony konflikt jak ten:

public class AbstractController {

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

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

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

Edit: Po kolejnym pytaniu zauważyłem, że dostajesz błędy w swojej opinii. Nie wiem, czy ten sposób złapie ten błąd..

Mam nadzieję, że to pomoże!!
 0
Author: Byron Voorbach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-28 13:43:21