1.问题描述

  项目正确的访问URL为:"http://172.18.40.230:10000/product/category/test/multi/slash",但当URL中多了斜杠时却仍然能够正常访问,例如"http://172.18.40.230:10000/product//category///test//multi//slash///"这个URL仍然可以正常访问对应的接口。

2.结论

  当request到达DispatcherServlet后,DispatcherServlet尝试从多个HandlerMapping中根据request获取HandlerExecutionChain。根据request获取,实际上就是根据request中的URI去获取,而SpringMVC获取request中的URI是通过调用UrlPathHelper中的getLookupPathForRequest()方法得到的。而getLookupPathForRequest()方法最终调用了getSanitizedPath()方法,该方法中将所有的//替换成了/

3.源码调用

注:以下源码基于SpringBoot的2.1.8.RELEASE版本。

// DispatcherServlet
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            // 尝试去获取HandlerExecutionChain
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

// AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 获取请求对应的处理方法
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

       /// more
}

// AbstractHandlerMethodMapping
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 获取请求对应的查找路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    this.mappingRegistry.acquireReadLock();
    try {
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

// UrlPathHelper
public String getLookupPathForRequest(HttpServletRequest request) {
    // Always use full path within current servlet context?
    if (this.alwaysUseFullPath) {
        return getPathWithinApplication(request);
    }
    // Else, use path within current servlet mapping if applicable
    String rest = getPathWithinServletMapping(request);
    if (!"".equals(rest)) {
        return rest;
    }
    else {
        return getPathWithinApplication(request);
    }
}
public String getPathWithinApplication(HttpServletRequest request) {
    String contextPath = getContextPath(request);
    String requestUri = getRequestUri(request);
    String path = getRemainingPath(requestUri, contextPath, true);
    if (path != null) {
        // Normal case: URI contains context path.
        return (StringUtils.hasText(path) ? path : "/");
    }
    else {
        return requestUri;
    }
}
public String getRequestUri(HttpServletRequest request) {
    String uri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
    if (uri == null) {
        uri = request.getRequestURI();
    }
    return decodeAndCleanUriString(request, uri);
}
private String decodeAndCleanUriString(HttpServletRequest request, String uri) {
    uri = removeSemicolonContent(uri);
    uri = decodeRequestString(request, uri);
    uri = getSanitizedPath(uri);
    return uri;
}
private String getSanitizedPath(final String path) {
    String sanitized = path;
    while (true) {
        int index = sanitized.indexOf("//");
        if (index < 0) {
            break;
        }
        else {
            sanitized = sanitized.substring(0, index) + sanitized.substring(index + 1);
        }
    }
    return sanitized;
}

4.测试

编写小demo并测试一下。

Last modification:April 11th, 2022 at 05:30 pm
如果觉得我的文章对你有用,请随意赞赏