7days-golang
7days-golang copied to clipboard
gee-web项目中 路由注册顺序,导致的Bug
func TestGetRoute(t *testing.T) {
r := newRouter()
r.addRoute("GET", "/", nil)
r.addRoute("GET", "/hello/:id", nil)
r.addRoute("GET", "/hello/b/c", nil)
n, ps := r.getRoute("GET", "/hello/21/c")
fmt.Printf("n: %v\n", n) // n: node{pattern=/hello/b/c, part=c, isWild=false}
fmt.Printf("ps: %v\n", ps)//ps: map[]
}
我们期望得到 n == nil, ps == nil , 但实际上却匹配到了静态路由 pattern=/hello/b/c.
由于注册顺序导致的 路由树可以简化为: [0] root: path: / [1] path: hello [2] path: :id pattern : "/hello/:id" [3] path: c pattern : "hello/b/c"
主要原因为 先注册的 路由为 /hello/:id , 后注册的路由为 /hello/b/c , 在查找时,没有返回 nil, 源代码如下:
func (n *node) matchChild(part string) *node {
for _, child := range n.children {
if child.part == part || child.isWild { // false, true
// 在查找 part为 :id 这一层时, 直接返回了child
return child
}
}
return nil
}
func (n *node) insert(pattern string, parts []string, height int) {
if len(parts) == height {
n.pattern = pattern
return
}
part := parts[height]
child := n.matchChild(part) // 由于返回的不是 nil , 而是 node.part == ":id" 这个节点。
if child == nil {
child = &node{part: part, isWild: part[0] == ':' || part[0] == '*'}
n.children = append(n.children, child)
}
child.insert(pattern, parts, height+1)
}
要想达到预期 结果 n == nil, ps == nil , 就只能修改注册顺序。
先注册静态路由, 确保 matchChildren(part string) []*node 方法返回的切片 包含 child.part="b" 的这个节点。
r.addRoute("GET", "/hello/b/c", nil)
r.addRoute("GET", "/hello/:id", nil)