7days-golang icon indicating copy to clipboard operation
7days-golang copied to clipboard

gee-web项目中 路由注册顺序,导致的Bug

Open FormalYou opened this issue 1 year ago • 3 comments

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)

FormalYou avatar Nov 21 '22 19:11 FormalYou