Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle exceptions thrown while rendering a view in Spring MVC?

I have a Spring MVC application which uses FreeMarker as View technology (But maybe the view technology doesn't really matter for my question). I need to intercept all exceptions which may get thrown during a request.

I have implemented a HandlerExceptionResolver but this resolver is only executed when the exception occurs within a controller. But when a controller returns a ModelAndView and the exception occurs while rendering the view (Because a variable was not found or something like this) then the exception resolver is not called and instead I get a stack trace in the browser window.

I also tried using an exception handler method within the controller which returns the view and annotated it with @ExceptionHandler but this also doesn't work (Most likely again because the exception is not thrown in the controller but in the view).

So is there some Spring mechanism where I can register an exception handler which captures view errors?

like image 736
kayahr Avatar asked Jun 28 '12 13:06

kayahr


People also ask

How do you handle exceptions in Spring MVC?

Spring MVC Framework provides following ways to help us achieving robust exception handling. Controller Based - We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes Exception class as argument.

How do you handle exceptions in Spring framework?

Spring MVC provides exception handling for your web application to make sure you are sending your own exception page instead of the server-generated exception to the user. The @ExceptionHandler annotation is used to detect certain runtime exceptions and send responses according to the exception.

Which exception class is related to all the exceptions that are thrown in Spring applications?

all the exceptions thrown by the spring jdbc framework are subclasses of dataaccessexception which is a type of runtimeexception, so you need not handle it explicitly. any checked exception when thrown will be mapped to any of the subclasses of the dataaccessexception by the framework.


2 Answers

A word upfront: if you just need a "static" error page without much logic and model preparation, it should suffice to put a <error-page>-Tag in your web.xml (see below for an example).

Otherwise, there might be better ways to do this, but this works for us:

We use a servlet <filter> in the web.xml that catches all Exceptions and calls our custom ErrorHandler, the same we use inside the 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> 

The implementation looks essentially like this:

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

I believe this should work pretty much the same for FreeMarker templates. Of course if your error view throws an error, you're more or less out of options.

To also catch errors like 404 and prepare the model for it, we use a filter that is mapped to the 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> 

The doFilter-Implementation looks like this:

@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); } 
like image 145
oxc Avatar answered Oct 09 '22 23:10

oxc


You could extends the DispatcherServlet.

In your web.xml replace the generic DispatcherServlet for your own class.

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

Later create your own class DispatcherServletHandler and extends from 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);         }      } } 

And in that page we only have to show a message to the user.

like image 23
cralfaro Avatar answered Oct 09 '22 22:10

cralfaro