2012-06-28 9 views
23

Tengo una aplicación Spring MVC que usa FreeMarker como tecnología de visualización (Pero tal vez la tecnología de visualización realmente no importa para mi pregunta). Necesito interceptar todas las excepciones que puedan lanzarse durante una solicitud.¿Cómo manejar las excepciones lanzadas al renderizar una vista en Spring MVC?

He implementado un HandlerExceptionResolver pero este resolver solo se ejecuta cuando la excepción se produce dentro de un controlador. Pero cuando un controlador devuelve un ModelAndView y se produce la excepción al representar la vista (debido a que no se encontró una variable o algo así), entonces no se llama a la resolución de excepciones y en su lugar obtengo un seguimiento de la pila en la ventana del navegador.

También intenté usar un método de controlador de excepción dentro del controlador que devuelve la vista y lo anoté con @ExceptionHandler pero esto tampoco funciona (lo más probable es que la excepción no se arroje en el controlador sino en la vista) .

Entonces, ¿hay algún mecanismo Spring en el que pueda registrar un manejador de excepciones que capture errores de visualización?

+0

¿Ayudaría tal [configuración] (http://developingdeveloper.wordpress.com/2008/03/09/handling-exceptions-in-spring-mvc-part-2/)? – nobeh

+0

@nobeh No, desafortunadamente no. Este artículo simplemente explica el uso de las cosas HandlerExceptionResolver. Eso es lo que ya uso, pero solo captura las excepciones lanzadas en los controladores, no en las vistas. – kayahr

Respuesta

21

Una palabra por adelantado: si solo necesita una página de error "estática" sin mucha lógica y preparación del modelo, debería bastar con poner <error-page> -Tag en su web.xml (vea más abajo un ejemplo).

De lo contrario, puede haber mejores maneras de hacer esto, pero esto funciona para nosotros:

se utiliza un servlet <filter> en el web.xml que las capturas de todas las excepciones y llama a nuestra costumbre ManejadorError, la misma que utilizamos dentro de la Primavera 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> 

La aplicación se ve esencialmente como esto:

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

    // ... 
} 

creo que esto debería funcionar más o menos la misma para las plantillas FreeMarker. Por supuesto, si su vista de error arroja un error, estará más o menos sin opciones.

también para detectar errores como el 404 y el modelo listo para ello, se utiliza un filtro que está asignado a la ERROR despachador:

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

El doFilter-implementación es el siguiente:

@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

No he usado el controlador de error personalizado. Todo lo que quiero hacer es: si el procesamiento de la plantilla falla, entonces redirija al usuario a una página de error estática. El navegador muestra el contenido de la página de error, pero también muestra el seguimiento de la pila. ¿Alguna pista? Estoy usando Spring. Simplemente servlets con freemarker para vistas. – sbidwai

+1

Hola, gracias por esto, obtengo 'org.springframework.beans.factory.NoSuchBeanDefinitionException: Ningún bean llamado 'defaultErrorHandler' se define' en el init. ¿Debo definir esto en un servlet.xml? – Ernest

+0

¿Hay alguna manera de hacer esto adelante: 'request.getRequestDispatcher ("/WEB-INF/jsp/error/dispatch-error.jsp "). Reenviar (solicitud, respuesta);' utilizando un ViewResolver existente? – FelipeKunzler

0

No estoy seguro si mi solución funciona con el problema que está teniendo. Enfermedad apenas publicar el camino Recupero excepciones para asegurar que ningún seguimiento de la pila es espectáculo dentro del navegador:

Hice una clase AbstractController con un método que controlará un conflicto específico de esta manera:

public class AbstractController { 

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

esta manera cada vez que se produce una excepción, el usuario verá un estado HTTPResponse predeterminado. (por ejemplo, 404 Not Found, etc.)

Extiendo esta clase en todas mis clases de controlador para asegurarme de que los errores se redirijan al AbstractController. De esta forma, no necesito usar ExceptionHandler en un controlador específico, pero puedo agregarlo globalmente a todos mis controladores. (extendiendo la clase AbstractController).

Editar: Después de otra pregunta, noté que está recibiendo errores en su punto de vista. No estoy seguro de si de esta manera detectará ese error ...

Espero que esto ayude !!

+0

Ya lo intenté. Agregué ese método directamente en el controlador y no en una clase base de él, pero esto no hace la diferencia. Se llama a este manejador de excepciones cuando se lanza la excepción dentro del método del controlador, pero no cuando se lanza la excepción en la vista. – kayahr

+0

o si el error ocurre en un filtro de servlet. Creo que la respuesta aceptada es la única que maneja cualquier error en cualquier lugar. – ticktock

+0

Esto no funcionará. El procesamiento de la plantilla ocurre DESPUÉS de que se invoquen los controladores y el consejo del controlador. Así que la única forma en que puedo pensar es agregar handline para filtrar según lo sugerido por la respuesta aceptada. No es bonito, pero tiene sentido. –

6

Podría extender el DispatcherServlet.

En su web.xml reemplace el DispatcherServlet genérico para su propia clase.

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

Posteriormente crear su propia clase DispatcherServletHandler y se extiende desde 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); 
     } 
    } 
} 

Y en esa página sólo tenemos que mostrar un mensaje al usuario.

Cuestiones relacionadas