gin的路由例子3

2021-07-28  本文已影响0人  xiaolv17

这篇文章探索一下加一个参数路由是怎么加的(/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

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

上一篇下一篇

猜你喜欢

热点阅读