您现在的位置是:首页 >技术杂谈 >GoWeb之静态路由查找(三)网站首页技术杂谈
GoWeb之静态路由查找(三)
简介GoWeb之静态路由查找(三)
书接上回,我们成功实现了一个简单的Server,将里面的路由树成功定义出来,并且实现了注册功能,那么今天,我们就继续来学习,如何进行静态路由查找。
一、路由查找
路由查找是当我们注册了路由之后,如果我们要执行对应的 handleFunc 就需要进入到叶子节点中,找到对应的方法并执行。
所以,我们一般都是确保深度优先,一直向下搜索
1. 代码实现
在上节内容中,我们定义了 addRoute 方法,这次我们要定义 FindRoute 进行查找
话不多说,代码如下
func (r *router) FindRoute(method string, path string) (*node, bool) {
// 查找是否有该请求方法,有的话就拿到整条链路
root, ok := r.trees[method]
if !ok {
return nil, false
}
if path == "/" {
return root, true
}
// 前面要求 path必须是 /xxx/xxx
// 把前置和后置的斜杠都去掉
path = strings.Trim(path, "/")
segs := strings.Split(path, "/")
for _, seg := range segs {
// 查找的过程
c, found := root.childOf(seg)
if !found {
return nil, false
}
// 不断深入查找
root = c
}
return root, true
}
func (n *node) childOf(path string) (*node, bool) {
if n.children == nil {
return nil, false
}
child, ok := n.children[path]
return child, ok
}
需要注意的是:
- 根节点因为我们有切割和替换功能,所以需要单独进行处理
- 前面 add 的时候就已经进行过校验,所以查找的时候,不需要进行多余的校验,默认都是合法的
2. 测试用例
这里采用单元测试的方式,不想看可以直接无视
func (n *node) equal(y *node) (string, bool) {
if n.path != y.path {
return fmt.Sprintf("节点路径不匹配"), false
}
if len(n.children) != len(y.children) {
return fmt.Sprintf("子节点数量不匹配"), false
}
nHandler := reflect.ValueOf(n.handler)
yHandler := reflect.ValueOf(y.handler)
if nHandler != yHandler {
return fmt.Sprintf("handlerFunc 不匹配"), false
}
for k, c := range n.children {
dst, ok := y.children[k]
if !ok {
return fmt.Sprintf("子节点 %s 不存在", k), false
}
msg, ok := c.equal(dst)
if !ok {
return msg, false
}
}
return "", true
}
func TestRouter_FindRoute(t *testing.T) {
testRoutes := []struct {
method string
path string
}{
{
method: http.MethodGet,
path: "/user/home",
},
}
r := newRouter()
var mockHandler HandleFunc = func(ctx Context) {}
for _, route := range testRoutes {
r.addRoute(route.method, route.path, mockHandler)
}
testCases := []struct {
name string
method string
path string
// 预期有没有找到
wantFound bool
// 预期返回的node
wantNode *node
}{
{
// 完全命中
// 我们这个查找只要叶子节点,也就是最末端的
name: "user home",
method: http.MethodGet,
path: "/user/home",
wantFound: true,
wantNode: &node{
path: "home",
handler: mockHandler,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
n, found := r.FindRoute(tc.method, tc.path)
assert.Equal(t, tc.wantFound, found)
if !found {
return
}
// node 里面有方法,所以不能直接用 assert.Equal 来比较
equal, ok := n.equal(tc.wantNode)
assert.True(t, ok, equal)
})
}
}
要自己定义一个 关于 node 的equal 函数是因为,在 node 里面包含 handlefunc,函数无法进行比较,只能用反射进行对比,所以我们自己定义一个 equal 函数,而不是使用assert
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。





QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
U8W/U8W-Mini使用与常见问题解决
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结