gin的路由例子2
上篇文章谈了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接下来会讲述如果插入一个有参数的路由会是怎么样的。
这就是添加第一个请求路由的过程,上述内容入如有错误和不妥之处,请指出,望不吝赐教,谢谢。