首页 > 其他分享 >遇到的一个URL解析问题,以及解决方案

遇到的一个URL解析问题,以及解决方案

时间:2023-04-20 10:57:19浏览次数:29  
标签:null return lookupPath URL 解决方案 request handler pattern 解析

问题抛出

  

   如图中的HTTP接口,如果客户端通过/test/next/访问的时候,会进入到哪个处理方法中呢?

   答案是会走入第一个接口,springmvc会把next当做第一个接口的参数传进去,虽然在接口设计的时候可以通过参数校验或者数据校验来确保接口的功能正确,不过这种乌龙请求,springmvc还是给我们提供了一些解决方案。

  

源码解析

  org.springframework.web.servlet.DispatcherServlet#doDispatch

  这个方法是处理HTTP请求转发的,所有请求到server的都会先经过这个方法。在springmvc启动的时候,会将有controller注解(或者restController)下的method,都保存为他们成为handler的东西,

在寻找具体的handler方法,就是这个getHandler(),它已添加了注释。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                ......省略
    /**
     * Return the HandlerExecutionChain for this request.
     * <p>Tries all handler mappings in order.
     * @param request current HTTP request
     * @return the HandlerExecutionChain, or {@code null} if no handler could be found
     */
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

  debug进入到最核心的方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal

  这个方法主要是获取锁,然后进入到lookupHandlerMethod,看名字就知道是最重要的方法了。

    // Handler method lookup

    /**
     * Look up a handler method for the given request.
     */
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        if (logger.isDebugEnabled()) {
            logger.debug("Looking up handler method for path " + lookupPath);
        }
        this.mappingRegistry.acquireReadLock();
        try {
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            if (logger.isDebugEnabled()) {
                if (handlerMethod != null) {
                    logger.debug("Returning handler method [" + handlerMethod + "]");
                }
                else {
                    logger.debug("Did not find handler method for [" + lookupPath + "]");
                }
            }
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }

   org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

   这个方法的总体设计分为两步,最重要的2步。首先通过url直接获取method,如果获取不到就go through all。

   因为直接请求/test/next/,获取不到对应的handler,所以他go through all的时候,匹配的pattern匹配到/test/{parameter}这个接口,所以它走了乌龙请求。

    /**
     * Look up the best-matching handler method for the current request.
     * If multiple matches are found, the best match is selected.
     * @param lookupPath mapping lookup path within the current servlet mapping
     * @param request the current request
     * @return the best-matching handler method, or {@code null} if no match
     * @see #handleMatch(Object, String, HttpServletRequest)
     * @see #handleNoMatch(Set, String, HttpServletRequest)
     */
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            Collections.sort(matches, comparator);
            if (logger.isTraceEnabled()) {
                logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
                        lookupPath + "] : " + matches);
            }
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                            request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
                }
            }
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

    还有一个重要方法 org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingPattern

    这里标志了两个配置,一个是是否使用后缀匹配,一个是是否支持'/'结尾的请求

    private String getMatchingPattern(String pattern, String lookupPath) {
        if (pattern.equals(lookupPath)) {
            return pattern;
        }
        if (this.useSuffixPatternMatch) {
            if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
                for (String extension : this.fileExtensions) {
                    if (this.pathMatcher.match(pattern + extension, lookupPath)) {
                        return pattern + extension;
                    }
                }
            }
            else {
                boolean hasSuffix = pattern.indexOf('.') != -1;
                if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
                    return pattern + ".*";
                }
            }
        }
        if (this.pathMatcher.match(pattern, lookupPath)) {
            return pattern;
        }
        if (this.useTrailingSlashMatch) {
            if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
                return pattern +"/";
            }
        }
        return null;
    }

 

解决方案

  由于它拿不到直接映射而导致的遍历,那么就给他添加一个直接映射就可以了,这是最优的解决方案。

    有了这个映射缓存,在第一步获取直接映射的时候,就拿到了这个handler,不会走遍历了,这样就能准确识别接口了。

  第二种解决方案,就是禁用‘/’结尾的请求,这种方式会让/test/next/请求变成404,SpringBoot下继承这个类,它提供了mvc的基本配置。

@Configuration
public class SpnWebMvcConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseTrailingSlashMatch(false);
    }

}

 

标签:null,return,lookupPath,URL,解决方案,request,handler,pattern,解析
From: https://www.cnblogs.com/chentingk/p/17335975.html

相关文章

  • 手把手逐步解析Javaweb登录实例
    一、编写前端界面<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document</titl......
  • 前端跨域解决方案——JSONP
    JSONP(JSONwithPadding)是一种跨域请求的解决方案,它允许在不受同源策略限制的情况下从一个域中向另一个域中请求数据。JSONP的基本原理是利用<script>标签的src属性没有跨域限制的特性来实现跨域数据访问。在使用JSONP时,需要在客户端创建一个script标签,将需要访问的资......
  • ip addr命令解析
    转载请注明出处:1.ip addr命令使用解析ipaddr 命令是Linux系统中的一个网络管理工具,用于显示和配置系统中的网络接口及其地址信息。它可以列出系统中所有的网络接口及其详细信息,包括接口名称、MAC地址、IP地址、子网掩码、广播地址、网络类型、状态、传输单元大小等。......
  • elasticsearch 8集群搭建并完成CDN日志收集和解析
     谷歌一下,搜索结果有各种样的博客文章,看了很多篇,反而被绕晕,经过自己的实践搭建完成获取到的经验最适合自己,在这里把整个过程记录下来,也希望可以帮助到一些需要的人。搭建前,请阅读官方文档,虽然是英文,如果能看懂你能学到更精髓的部分。英文好坏也是技术和技术之间的分水岭。好的技......
  • 防患于未然,华为云数据灾备解决方案保护企业数据安全
    失去服务能力、影响业务,而不论是电力中断、网络故障、硬件故障,还是人为操作失误或恶意破坏,以及自然灾害等都有可能导致这一“灾难”的发生,所以为了保证数据库稳定运行、损失降到最低,提前进行容灾备份是十分有必要的。 你可能会问,什么是容灾?什么又是备灾?事实上,容灾指的是当灾害发生......
  • SpringMvc 原理解析
    springMVC源码流程第一步先来到DispatcherServlet()@SuppressWarnings("serial")publicclassDispatcherServletextendsFrameworkServlet来到doDispatch的方法doDispatch(HttpServletRequestrequest,HttpServletResponseresponse)进入//1.先检查时候文件上传请......
  • admin项目公共方法解析
    前言:项目中公用的一些方法,配置,常量等正文:文件:common/inc.go packagecommonconstTimeTem="2006-01-0215:04:05"constAdminSecret="jO4s4QcGs4B8brP2"//随机秘钥//定义一个统一的返回对象typeReDatastruct{StatusboolMsgstringData......
  • 高斯混合模型疑点解析
    高斯混合模型是EM算法的优秀实践,表达形式也十分简单,但是其推导确实有点复杂。推荐几篇不错的文章:(26条消息)ML-朴素贝叶斯-先验分布/后验分布/似然估计_特征条件独立性假设_透明的胡萝卜的博客-CSDN博客 (此篇文章介绍了一些朴素贝叶斯基本知识,建议先看)高斯混合模型(GMM)推导及......
  • 图数据库 NebulaGraph 的 Java 数据解析实践与指导
    如何快速、即时、符合直觉地去处理NebulaJavaClient中的数据解析?读这一篇就够了。图数据库NebulaGrpah的论坛和微信群里,有不少用户问及了Java客户端数据解析的问题。在本文教你一种简单的方式同返回结果交互,快速、即时地拿到解析数据。愉快、干净的Java交互环境本......
  • javasec(五)URLDNS反序列化分析
    这篇文章介绍URLDNS就是ysoserial中⼀个利⽤链的名字,但准确来说,这个其实不能称作“利⽤链”。因为其参数不是⼀个可以“利⽤”的命令,⽽仅为⼀个URL,其能触发的结果也不是命令执⾏,⽽是⼀次DNS请求。ysoserial打包成jar命令mvncleanpackage-DskipTests,刚刚入门所以用这条链作......