gin的路由例子2

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

上篇文章谈了gin添加一个路由/hello的过程,现在我们来添加第二个路由,第二个路由同样也是GET请求,路径可以是/hi或者是/ping。

一些前面的相同的过程不再列出,代码直接走到addRoute()方法.

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    assert1(path[0] == '/', "path must begin with '/'")
    assert1(method != "", "HTTP method can not be empty")
    assert1(len(handlers) > 0, "there must be at least one handler")

    debugPrintRoute(method, path, handlers)

    root := engine.trees.get(method)
    if root == nil {
        root = new(node)
        root.fullPath = "/"
        engine.trees = append(engine.trees, methodTree{method: method, root: root})
    }
    root.addRoute(path, handlers)

    // Update maxParams
    if paramsCount := countParams(path); paramsCount > engine.maxParams {
        engine.maxParams = paramsCount
    }
}

因为上面我们已经添加过一个路由,所以在root := engine.trees.get(method)这一步取GET请求的root节点的时候,root不为空,所以直接调用tree包的addRoute()函数,这个时候addRoute()的参数是我c们path是”/hi“(可以是别的)和handlers。此时我们root的结构是

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

接下来来贴一下addRoute的代码

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
    }
}

因为node的path已经有了值,所以走到下面的for循环中。首先调用longestCommonPrefix(path, n.path)找到当前请求url和节点path的最长相同部分的下标值。

如果我们要添加的请求是"/hi",那么i的值就是2,如果请求时/ping,那么i的值就是1

如果path的长度比下标值大的话,我们上面两个例子都是成立的,那么就会新建一个节点,这个节点的值是

type node struct {
    path      "i"
    indices   ""
    wildChild false
    nType     nodeType
    priority  1
    children  []
    handlers  handlers
    fullPath  "/hi"
}

或者是

type node struct {
    path      "ping"
    indices   ""
    wildChild false
    nType     nodeType
    priority  1
    children  []
    handlers  handlers
    fullPath  "/ping"
}

将这个节点赋值给当前根节点的children,改变节点的indices值、path值,将handler置为空等操作,此时根节点的值为

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

或者是

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

如果我们要添加的url比i大的话,把path的i后面的字符串赋值给path,因为根节点的wildChild是false,所以if的条件不成立,又因为节点不是参数节点所以第二个if条件也不成立,然后开始判断indices,因为我们的根节点的indices是有值的,所以走进这个for循环,这个for循环就是检测看看是不是还存在会有前缀相吻合的路径,如果有的话那就重复上面的动作,如果没有相同的话那就给刚刚的节点赋上请求handler。如果刚刚c不是:或者*就把indices也加进去。最后这个树应该是这样的。

未命名文件 (3).png

或者

未命名文件 (2).png

接下来会讲述如果插入一个有参数的路由会是怎么样的。

这就是添加第一个请求路由的过程,上述内容入如有错误和不妥之处,请指出,望不吝赐教,谢谢。

上一篇下一篇

猜你喜欢

热点阅读