如何处理在Spring MVC中呈现视图时抛出的异常?

时间:2012-06-28 13:37:39

标签: java spring spring-mvc freemarker

我有一个Spring MVC应用程序,它使用FreeMarker作为View技术(但也许视图技术对我的问题并不重要)。我需要拦截在请求期间可能抛出的所有异常。

我已经实现了HandlerExceptionResolver,但只有在控制器中发生异常时才会执行此解析程序。但是当控制器返回一个ModelAndView并且在渲染视图时发生异常(因为找不到变量或类似的东西),则不会调用异常解析器,而是在浏览器窗口中获得堆栈跟踪。

我还尝试在控制器中使用异常处理程序方法,该方法返回视图并使用@ExceptionHandler对其进行注释,但这也不起作用(很可能是因为异常不会在控制器中抛出但在视图中)。

那么是否有一些Spring机制可以注册一个捕获视图错误的异常处理程序?

3 个答案:

答案 0 :(得分:21)

前言:如果您只需要一个“静态”错误页面而没有太多的逻辑和模型准备,那么在<error-page>中添加web.xml - 标记就足够了(请参阅下面的示例) )。

否则,可能有更好的方法来做到这一点,但这对我们有用:

我们在<filter>中使用servlet web.xml捕获所有异常并调用我们的自定义ErrorHandler,就像我们在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>

实现看起来基本上是这样的:

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

  // ...
}

我相信这应该对FreeMarker模板几乎一样。当然,如果您的错误视图引发错误,那么您或多或少会有选择。

为了捕获404之类的错误并为其准备模型,我们使用映射到ERROR调度程序的过滤器:

<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-Implementation看起来像这样:

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

答案 1 :(得分:7)

您可以扩展DispatcherServlet。

在您的web.xml中替换您自己的类的通用DispatcherServlet。

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

稍后创建自己的类DispatcherServletHandler并从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);
        }
     }
}

在该页面中,我们只需向用户显示一条消息。

答案 2 :(得分:0)

不确定我的解决方案是否适用于您遇到的问题。我只是发布我捕获异常的方式,以确保浏览器中没有显示堆栈跟踪:

我创建了一个AbstractController类,其中包含一个处理特定冲突的方法:

public class AbstractController {

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

这样,只要发生异常,用户就会看到默认的HTTPResponse状态。 (例如,404 Not Found等。)

我在所有控制器类上扩展此类,以确保将错误重定向到AbstractController。这样我就不需要在特定的控制器上使用ExceptionHandler,但我可以将全局添加到我的所有控制器。 (通过扩展AbstractController类)。

修改 继续你的问题后,我注意到你的观点出现了错误。不确定这种方式是否会捕获该错误..

希望这会有所帮助!!