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

gin的路由例子2

作者: xiaolv17 | 来源:发表于2021-07-25 05:11 被阅读0次

    上篇文章谈了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

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

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

    相关文章

      网友评论

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

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