美文网首页
gin的路由例子3

gin的路由例子3

作者: xiaolv17 | 来源:发表于2021-07-28 02:17 被阅读0次

    这篇文章探索一下加一个参数路由是怎么加的(/hello/:name),我们拿上篇文章的例子继续,就是已经路由树是下图这样的。

    未命名文件 (3).png

    之前的一些通常的操作就不看了(可以看前面的文章),直接进入addRoute()方法。

    // addRoute adds a node with the given handle to the path.
    // Not concurrency-safe!
    func (n *node) addRoute(path string, handlers HandlersChain) {
        fullPath := path
        n.priority++
    
        // Empty tree
        if len(n.path) == 0 && len(n.children) == 0 {
            n.insertChild(path, fullPath, handlers)
            n.nType = root
            return
        }
    
        parentFullPathIndex := 0
    
    walk:
        for {
            // Find the longest common prefix.
            // This also implies that the common prefix contains no ':' or '*'
            // since the existing key can't contain those chars.
            //找到路径中最长的相同的部分
            //当然这不包含:或者*
            i := longestCommonPrefix(path, n.path)
    
            // Split edge
            if i < len(n.path) {
                //添加给n的children节点
                child := node{
                    path:      n.path[i:],
                    wildChild: n.wildChild,
                    indices:   n.indices,
                    children:  n.children,
                    handlers:  n.handlers,
                    priority:  n.priority - 1,
                    fullPath:  n.fullPath,
                }
    
                n.children = []*node{&child}
                // []byte for proper unicode char conversion, see #65
                n.indices = bytesconv.BytesToString([]byte{n.path[i]})
                n.path = path[:i]
                n.handlers = nil
                n.wildChild = false
                n.fullPath = fullPath[:parentFullPathIndex+i]
            }
    
            // Make new node a child of this node
            if i < len(path) {
                //不一样的部分
                path = path[i:]
    
                if n.wildChild {
                    //如果有通配符节点
                    parentFullPathIndex += len(n.path)
                    n = n.children[0] //取得第一个子节点
                    n.priority++
    
                    // Check if the wildcard matches
                    //检查是否有匹配的通配符节点
                    if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
                        // Adding a child to a catchAll is not possible
                        n.nType != catchAll &&
                        // Check for longer wildcard, e.g. :name and :names
                        (len(n.path) >= len(path) || path[len(n.path)] == '/') {
                        continue walk
                    }
    
                    pathSeg := path
                    if n.nType != catchAll {
                        pathSeg = strings.SplitN(path, "/", 2)[0]
                    }
                    prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
                    panic("'" + pathSeg +
                        "' in new path '" + fullPath +
                        "' conflicts with existing wildcard '" + n.path +
                        "' in existing prefix '" + prefix +
                        "'")
                }
    
                c := path[0]
    
                // slash after param
                //参数后面的/
                if n.nType == param && c == '/' && len(n.children) == 1 {
                    parentFullPathIndex += len(n.path)
                    n = n.children[0]
                    n.priority++
                    continue walk
                }
    
                // Check if a child with the next path byte exists
                for i, max := 0, len(n.indices); i < max; i++ {
                    if c == n.indices[i] {
                        parentFullPathIndex += len(n.path)
                        i = n.incrementChildPrio(i)
                        n = n.children[i]
                        continue walk
                    }
                }
    
                // Otherwise insert it
                if c != ':' && c != '*' {
                    // []byte for proper unicode char conversion, see #65
                    n.indices += bytesconv.BytesToString([]byte{c})
                    child := &node{
                        fullPath: fullPath,
                    }
                    n.children = append(n.children, child)
                    n.incrementChildPrio(len(n.indices) - 1)
                    n = child
                }
                n.insertChild(path, fullPath, handlers)
                return
            }
    
            // Otherwise and handle to current node
            if n.handlers != nil {
                panic("handlers are already registered for path '" + fullPath + "'")
            }
            n.handlers = handlers
            n.fullPath = fullPath
            return
        }
    }
    

    这个方法的节点是Get方法的根节点,此时根节点的结构是这样的

    type node struct {
        path      "/h"
        indices   "ei"
        wildChild false
        nType     root
        priority  3
        children  //刚创建的node
        handlers  nil
        fullPath  "/h"
    }
    

    此时函数中的pullPath的值是/hello/:name,parentFullPathIndex的值是0,进入for循环之后调用longestCommonPrefix()函数,找到/hello/:name和/h的最长的相同部分,得到结果i的值为2,那么第一个if条件就不成立,进入第二个if条件之后path的被更新为ello/:name,因为此时n的nType是root,所以也不成立,c的值为e,然后就进入一下循环

    for i, max := 0, len(n.indices); i < max; i++ {
                    if c == n.indices[i] {
                        parentFullPathIndex += len(n.path)
                        i = n.incrementChildPrio(i)
                        n = n.children[i]
                        continue walk
                    }
                }
    
    // Increments priority of the given child and reorders if necessary
    func (n *node) incrementChildPrio(pos int) int {
        cs := n.children
        cs[pos].priority++
        prio := cs[pos].priority
    
        // Adjust position (move to front)
        newPos := pos
        for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- {
            // Swap node positions
            cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1]
        }
    
        // Build new index char string
        if newPos != pos {
            n.indices = n.indices[:newPos] + // Unchanged prefix, might be empty
                n.indices[pos:pos+1] + // The index char we move
                n.indices[newPos:pos] + n.indices[pos+1:] // Rest without char at 'pos'
        }
    
        return newPos
    }
    

    此处就是寻找寻找插入节点和已有子节点是否有相同的path的开头,因为c的值e,在根节点的子结点中第0个子节点的path为ello,是一样的,将该子节点赋值为n,然后重新开始for循环,这个时候n节点数据是

    type node struct {
        path      "ello"
        indices   ""
        wildChild false
        nType     nType
        priority  2
        children  []
        handlers  handlers
        fullPath  "/hello"
    }
    

    for循环开始依旧是找出两个路径的最长相同部分的长度,只是说这次的path是ello/:name和ello,所以i的值为4,第一个if条件依旧不满足,新的path=path[i:],path的值就是/:name,if条件不满足之后,c:=path[0],c的值是/,然后if条件和for循环的if都不满足,走到了下面一个if条件

    // Otherwise insert it
                if c != ':' && c != '*' {
                    // []byte for proper unicode char conversion, see #65
                    n.indices += bytesconv.BytesToString([]byte{c})
                    child := &node{
                        fullPath: fullPath,
                    }
                    n.children = append(n.children, child)
                    n.incrementChildPrio(len(n.indices) - 1)
                    n = child
                }
                n.insertChild(path, fullPath, handlers)
                return
    

    更改n节点的indices的值为/,新建一个了节点fullPath的值为/hello/:name,然后把新建的节点放在n的子节点里面,之后刚刚新建的节点结构式

    type node struct {
        path      ""
        indices   ""
        wildChild false
        nType     nType
        priority  1
        children  []
        handlers  
        fullPath  "/hello/:name"
    }
    

    最后调用insertChild(/:name,/hello/:name,handlers)方法

    func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) {
        for {
            // Find prefix until first wildcard
            //找到通配符后路径字符串并返回其位置判断path的通配符是否合法
            wildcard, i, valid := findWildcard(path)
            //值为 :name,1,true
            if i < 0 { // No wildcard found
                break
            }
    
            // The wildcard name must not contain ':' and '*'
            if !valid {
                panic("only one wildcard per path segment is allowed, has: '" +
                    wildcard + "' in path '" + fullPath + "'")
            }
    
            // check if the wildcard has a name
            //判断通配符后面的字符串长度
            if len(wildcard) < 2 {
                panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
            }
    
            // Check if this node has existing children which would be
            // unreachable if we insert the wildcard here
            if len(n.children) > 0 {
                panic("wildcard segment '" + wildcard +
                    "' conflicts with existing children in path '" + fullPath + "'")
            }
    
            //如果是参数的话
            if wildcard[0] == ':' { // param
                if i > 0 {
                    // Insert prefix before the current wildcard
                    n.path = path[:i]   //结点的path就是:之前的
                    path = path[i:]     //把:之后的值赋值给path
                }
    
                n.wildChild = true  //将通配符子节点置为true
                child := &node{
                    nType:    param,    //参数类型
                    path:     wildcard, //通配符后面的字符串
                    fullPath: fullPath, //完整的路径
                }
                n.children = []*node{child} //把节点塞到子节点里面
                n = child
                n.priority++
    
                // if the path doesn't end with the wildcard, then there
                // will be another non-wildcard subpath starting with '/'
                //类似于:asd/ahshd
                if len(wildcard) < len(path) {
                    path = path[len(wildcard):]
    
                    child := &node{
                        priority: 1,
                        fullPath: fullPath,
                    }
                    n.children = []*node{child} //将后面的放在children里面
                    n = child
                    continue
                }
    
                // Otherwise we're done. Insert the handle in the new leaf
                n.handlers = handlers
                return
            }
    
            // catchAll
            if i+len(wildcard) != len(path) {
                panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
            }
    
            if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
                panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
            }
    
            // currently fixed width 1 for '/'、
            ///iu/kyut
            i--
            if path[i] != '/' {
                panic("no / before catch-all in path '" + fullPath + "'")
            }
            //通配符/前面的字符串
            n.path = path[:i]
    
            // First node: catchAll node with empty path
            //将/前面的作为一个子节点
            child := &node{
                wildChild: true,
                nType:     catchAll,
                fullPath:  fullPath,
            }
    
            n.children = []*node{child}
            n.indices = string('/')
            n = child
            n.priority++
    
            // second node: node holding the variable
            //将/后面的作为/前面的节点的子节点
            child = &node{
                path:     path[i:],
                nType:    catchAll,
                handlers: handlers,
                priority: 1,
                fullPath: fullPath,
            }
            n.children = []*node{child}
    
            return
        }
    

    由上面代码可知,又新建了一个节点,节点值为

    type node struct {
        path      "name"
        indices   ""
        wildChild false
        nType     param
        priority  1
        children  []
        handlers  handers
        fullPath  "/hello/:name"
    }
    

    然后将刚刚新建的节点放到children中,最终节点的值为

    type node struct {
        path  ":"   
            indices ""
        wildChild true
        nType     nType 
            priority  1 
            children  []*node //刚刚新建的节点 
            handlers    
            fullPath  "/hello/:name"}
    

    最后路由树

    未命名文件 (5).png

    上述内容如有错误和不妥之处,欢迎大家指出不吝赐教,谢谢。

    相关文章

      网友评论

          本文标题:gin的路由例子3

          本文链接:https://www.haomeiwen.com/subject/ftiamltx.html