Spring MVC异常处理

1       Spring  MVC 异常处理介绍

我们知道,Spring MVC中提供了处理异常的解析器(HandlerExceptionResolver),其意图是,当与请求匹配的控制器(Handler Controller)处理请求时,将发生的异常交由HandlerExceptionResolverde>来处理,从而给框架的使用者一个集中处理异常的机会。de>

Spring MVC提供了一个de>HandlerExceptionResolversde>de>de>简单实现SimpleMappingExceptionResolver,这个解析器将抛出异常的类名映射到一个视图名,通过配置,可以将不同的异常映射到不同的错误视图。

当然,你也可以实现一个自己的HandlerExceptionResolver只需实现

ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

方法, 并返回一个ModelAndView

 

2       Spring MVC 2.5.5问题描述

在做项目的过程中,我们发现Spring MVC 2.5.5的异常处理不是很好使,具体的表现就是,SimpleMappingExceptionResolver捕获了异常,并正确的将异常映射到视图(view),但是当视图渲染(render)完之后,结束了请求,浏览器中并没有显示相应的视图信息,而是熟悉的Tomcat 500页面,如下图:

Spring MVC异常处理 - dinstone - dinstone的代码人生!

1

这里可以确定的是,JSP视图页面肯定执行了(你可以在JSP中添加测试代码如:System.out.println("ok"),查看Tomcat控制台是否会输出“ok)。

 

3       深入剖析

经过跟踪Tomcat的源码,我们发现,TomcatHost Vale在响应请求时会检查请求是否包含了异常信息。代码org.apache.catalina.core.StandardHostValve.java片段如下:

// Error page processing

        response.setSuspended(false);

// EXCEPTION_ATTR =   "javax.servlet.error.exception";

        Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);

 

        if (t != null) {

            throwable(request, response, t);

        else {

            status(request, response);

        }

 

protected void throwable(Request request, Response response,

                             Throwable throwable) {

        Context context = request.getContext();

        if (context == null)

            return;

 

        Throwable realError = throwable;

 

        if (realError instanceof ServletException) {

            realError = ((ServletException) realError).getRootCause();

            if (realError == null) {

                realError = throwable;

            }

        }

 

        // If this is an aborted request from a client just log it and return

        if (realError instanceof ClientAbortException ) {

            if (log.isDebugEnabled()) {

                log.debug

                    (sm.getString("standardHost.clientAbort",

                        realError.getCause().getMessage()));

            }

            return;

        }

 

        ErrorPage errorPage = findErrorPage(context, throwable);

        if ((errorPage == null) && (realError != throwable)) {

            errorPage = findErrorPage(context, realError);

        }

 

        if (errorPage != null) {

            response.setAppCommitted(false);

            request.setAttribute

                (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,

                 errorPage.getLocation());

            request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,

                              new Integer(ApplicationFilterFactory.ERROR));

            request.setAttribute

                (Globals.STATUS_CODE_ATTR,

                 new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));

            request.setAttribute(Globals.ERROR_MESSAGE_ATTR,

                              throwable.getMessage());

            request.setAttribute(Globals.EXCEPTION_ATTR,

                              realError);

            Wrapper wrapper = request.getWrapper();

            if (wrapper != null)

                request.setAttribute(Globals.SERVLET_NAME_ATTR,

                                  wrapper.getName());

            request.setAttribute(Globals.EXCEPTION_PAGE_ATTR,

                                 request.getRequestURI());

            request.setAttribute(Globals.EXCEPTION_TYPE_ATTR,

                              realError.getClass());

            if (custom(request, response, errorPage)) {

                try {

                    response.flushBuffer();

                catch (IOException e) {

                    container.getLogger().warn("Exception Processing " + errorPage, e);

                }

            }

        else {

            // A custom error-page has not been defined for the exception

            // that was thrown during request processing. Check if an

            // error-page for error code 500 was specified and if so,

            // send that page back as the response.

            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

            // The response is an error

            response.setError();

 

            status(request, response);

        }

 

 

    }

 

当发现请求属性中包含异常信息时,就不会将先前渲染过的页面输出给客户端了,而是转而执行以下操作,首先Tomcat检查web.xml中的error配置,然后将500异常映射到指定的JSP页面,如果没有指定error配置,Tomcat则会使用如图1的页面输出响应。这样我们就看到了熟悉的500页面了。

那么这个是Tomcat的问题还是Spring MVC的问题呢?

首先,TomcatServlet/JSP实现是通过Servlet/JSP规范验证的,也就是说,其实现是遵循规范的。而规范也恰恰是这么规定的,所以可以初步的判断是Spring MVC2.5.5的问题了。

如何解决这个问题呢?由Servlet/JSP规范可知,我们可以清理请求中的属性,从而使得Servlet/JSP容器以为处理请求的过程中没有异常发生,响应的状态码为200就可以了。

 

4       解决方法

方法一:在异常处理JSP页面中添加以下代码:

    String ERROR_STATUS_CODE_ATTRIBUTE = "javax.servlet.error.status_code";

    String ERROR_EXCEPTION_TYPE_ATTRIBUTE ="javax.servlet.error.exception_type";

    String ERROR_MESSAGE_ATTRIBUTE = "javax.servlet.error.message";

    String ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception";

    String ERROR_REQUEST_URI_ATTRIBUTE ="javax.servlet.error.request_uri";

    String ERROR_SERVLET_NAME_ATTRIBUTE ="javax.servlet.error.servlet_name";

 

    request.removeAttribute(ERROR_STATUS_CODE_ATTRIBUTE);

    request.removeAttribute(ERROR_EXCEPTION_TYPE_ATTRIBUTE);

    request.removeAttribute(ERROR_MESSAGE_ATTRIBUTE);

    request.removeAttribute(ERROR_EXCEPTION_ATTRIBUTE);

    request.removeAttribute(ERROR_REQUEST_URI_ATTRIBUTE);

    request.removeAttribute(ERROR_SERVLET_NAME_ATTRIBUTE);

response.setStatus(HttpServletResponse.SC_OK);

 

方法二:升级到 Spring MVC2.5.6

Spring MVC 2.5.6中已经解决了这个问题。在DispatcherServlet类中,检查渲染的是否error视图,如果是则清空请求中的参数。如下图:

Spring MVC异常处理 - dinstone - dinstone的代码人生!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值