Spring MVC錯誤頁面配置

當前SpringMVC很是流行,在大多數狀況,咱們都須要自定義一些錯誤頁面(例如:401, 402, 403, 500...),以便更友好的提示。對於spring mvc,這些固然是支持自定義的,spring是怎麼作的? 仍是去看看spring的源碼吧:
html

原理

DispatcherServlet

衆所周知,springmvc的入口是DispatcherServlet, 在DispatcherServlet的源碼中,不知你是否注意到瞭如下方法:java

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
        Object handler, Exception ex) throws Exception {

    // Check registered HandlerExceptionResolvers...
    ModelAndView exMv = null;
    for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
        exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
        if (exMv != null) {
            break;
        }
    }
    if (exMv != null) {
        if (exMv.isEmpty()) {
            request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
            return null;
        }
        // We might still need view name translation for a plain error model...
        if (!exMv.hasView()) {
            exMv.setViewName(getDefaultViewName(request));
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
        }
        WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
        return exMv;
    }

    throw ex;
}

這個方法就是springmvc對於異常的處理,其調用了HandlerExceptionResolver的resolveException方法。HandlerExceptionResolver有衆多實現類,其中,重點看看
SimpleMappingExceptionResolver(咱們就是要經過它來配置自定義錯誤頁面)。web

SimpleMappingExceptionResolver

public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
    ...
    private Properties exceptionMappings;

    private Class<?>[] excludedExceptions;
    
    private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
    ...
    
    public void setExceptionMappings(Properties mappings) {
        this.exceptionMappings = mappings;
    }
    
    public void setStatusCodes(Properties statusCodes) {
        for (Enumeration<?> enumeration = statusCodes.propertyNames(); enumeration.hasMoreElements();) {
            String viewName = (String) enumeration.nextElement();
            Integer statusCode = new Integer(statusCodes.getProperty(viewName));
            this.statusCodes.put(viewName, statusCode);
        }
    }
    
}

@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
        Object handler, Exception ex) {

    // Expose ModelAndView for chosen error view.
    String viewName = determineViewName(ex, request);
    if (viewName != null) {
        // Apply HTTP status code for error views, if specified.
        // Only apply it if we're processing a top-level request.
        Integer statusCode = determineStatusCode(request, viewName);
        if (statusCode != null) {
            applyStatusCodeIfPossible(request, response, statusCode);
        }
        return getModelAndView(viewName, ex, request);
    }
    else {
        return null;
    }
}

protected String determineViewName(Exception ex, HttpServletRequest request) {
    String viewName = null;
    if (this.excludedExceptions != null) {
        for (Class<?> excludedEx : this.excludedExceptions) {
            if (excludedEx.equals(ex.getClass())) {
                return null;
            }
        }
    }
    // Check for specific exception mappings.
    if (this.exceptionMappings != null) {
        viewName = findMatchingViewName(this.exceptionMappings, ex);
    }
    // Return default error view else, if defined.
    if (viewName == null && this.defaultErrorView != null) {
        if (logger.isDebugEnabled()) {
            logger.debug("Resolving to default view '" + this.defaultErrorView + "' for exception of type [" +
                    ex.getClass().getName() + "]");
        }
        viewName = this.defaultErrorView;
    }
    return viewName;
}

protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
    String viewName = null;
    String dominantMapping = null;
    int deepest = Integer.MAX_VALUE;
    for (Enumeration<?> names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
        String exceptionMapping = (String) names.nextElement();
        int depth = getDepth(exceptionMapping, ex);
        if (depth >= 0 && (depth < deepest || (depth == deepest &&
                dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) {
            deepest = depth;
            dominantMapping = exceptionMapping;
            viewName = exceptionMappings.getProperty(exceptionMapping);
        }
    }
    if (viewName != null && logger.isDebugEnabled()) {
        logger.debug("Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() +
                "], based on exception mapping [" + dominantMapping + "]");
    }
    return viewName;
}

protected Integer determineStatusCode(HttpServletRequest request, String viewName) {
    if (this.statusCodes.containsKey(viewName)) {
        return this.statusCodes.get(viewName);
    }
    return this.defaultStatusCode;
}

因而可知:spring

SimpleMappingExceptionResolver經過 exceptionMappings和statusCodes來確立Exception、http狀態碼以及view之間的映射關係。明白這個就很簡單了,咱們能夠經過設置exceptionMappings、statusCodes的值來實現咱們自定義的映射關係。spring-mvc

實戰

頁面準備

  1. 咱們在WEB-INF/views/commons/error(目錄本身定)新建咱們自定義的錯誤頁面,404.html, 500.html等等。mvc

  2. SimpleMappingExceptionResolver只實現映射關係,咱們還須要經過配置web.xml來實現。app

    <error-page>
         <error-code>404</error-code>
         <location>/error/404.html</location>
     </error-page>
    
     <error-page>
         <error-code>500</error-code>
         <location>/error/500.html</location>
     </error-page>

  3. 在spring-mvc配置文件中將404.html、500.html等設置爲資源文件,避免被springmvc再次攔截。dom

    <mvc:resources mapping="/error/**" location="/WEB-INF/views/commons/error/" />
  4. 配置SimpleMappingExceptionResolver。ide

    <bean class="org.springframework.web.servlet.handler. SimpleMappingExceptionResolver">
         <property name="exceptionMappings">
             <map>
                 <entry key="ResourceNotFoundException" value="common/error/resourceNotFoundError" />
                 <entry key=".DataAccessException" value="common/error/dataAccessError" />
             </map>
         </property>
         <property name="statusCodes">
             <map>
                 <entry key="common/error/resourceNotFoundError" value="404" />
                 <entry key="common/error/dataAccessError" value="500" />
             </map>
         </property>
     </bean>

到此,就實現咱們須要的配置了。this

歡迎訪問個人獨立博客:

www.javafan.cn

相關文章
相關標籤/搜索