美文网首页
gin路由例子1

gin路由例子1

作者: xiaolv17 | 来源:发表于2021-07-24 08:34 被阅读0次

    上篇文章说将请求加入树中是比较关键的,现在我们开始举第一个例子说明。

    这个是最简单的例子,很多文章写gin的使用都会写到

    r := gin.Default()
        r.GET("/hello", func(c *gin.Context) {
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    

    现在我们就来看看这个/hello的请求是怎么添加到路由树中的。

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

    此时addRoute()三个参数的值分别是"GET","/hello",handlers,它会先去取GET请求的root节点,因为我们这个是第一个请求那么root就是空的,这个时候就会新建一个node作为GET请求的根节点,这个时候当前节点的结构如下:

    type node struct {
        path      string
        indices   string
        wildChild bool
        nType     nodeType
        priority  uint32
        children  []*node
        handlers  HandlersChain
        fullPath  "/"
    }
    

    紧接着就会调用addRoute()方法,将该请求加到树中。此时addRoute的参数path的值是"/hello"

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

    我们可以看到给根节点的priority加1;然后判断是不是一个空树,由上面的node的值判断,这里if条件是成立的,这个时候会调用insertChild(path,fullPath,handlers)函数,此时path和fullPath的值都是"/hello"。

    func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) {
        for {
            // Find prefix until first wildcard
            //找到:或者*后路径字符串并返回其位置判断之后还有没有:或者*
            wildcard, i, valid := findWildcard(path)
            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
        }
    
        // If no wildcard was found, simply insert the path and handle
        //如果没有通配符就直接插进去
        n.path = path
        n.handlers = handlers
        n.fullPath = fullPath
    }
    
    func findWildcard(path string) (wildcard string, i int, valid bool) {
        // Find start
        for start, c := range []byte(path) {
            // A wildcard starts with ':' (param) or '*' (catch-all)
            if c != ':' && c != '*' {
                continue
            }
    
            // Find end and check for invalid characters
            valid = true
            for end, c := range []byte(path[start+1:]) {
                switch c {
                case '/':
                    return path[start : start+1+end], start, valid
                case ':', '*':
                    valid = false
                }
            }
            return path[start:], start, valid
        }
        return "", -1, false
    }
    

    由我上面的注释可以看出insertChild函数会先判断有没有:或者*,因为我们的url为"/hello"不含这两个字符,所以就会直接返回,insertChild的for循环也会直接break,直接走到下面代码。

        n.path = path
        n.handlers = handlers
        n.fullPath = fullPath
    

    给根节点赋值,此时我们的根节点的结构为

    type node struct {
        path      "/hello"
        indices   string
        wildChild bool
        nType     nodeType
        priority  uint32
        children  []*node
        handlers  handlers
        fullPath  "/hello"
    }
    

    之后insertChild()返回,将根节点的nType的值置为root,直接返回,然后addRoute()函数更新参数数量。最后数据的结构为

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

    以上操作就已经把这个/hello请求加到了gin的路由树了,此时这棵GET树上只有一个节点。

    未命名文件.png

    下篇文章,我会举第二个例子,在已经有一个/hello节点的时候添加另一个节点的过程·。

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

    相关文章

      网友评论

          本文标题:gin路由例子1

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