3.07 路由树-通配符匹配之路由注册
本节课工程结构如下:
(base) yanglei@yuanhong 11-embeddingRouterIntoServer % tree ./
./
├── context.go
├── handleFunc.go
├── httpServer.go
├── httpServer_test.go
├── node.go
├── router.go
├── router_test.go
└── serverInterface.go
0 directories, 8 filesPART1. 学习路线

PART2. 通配符匹配的定义与设计
2.1 通配符匹配的定义
通配符匹配:是指用*表达匹配任何路径
2.2 通配符匹配的设计
这里就要考虑定义中所说的任何路径.
Question1:若用户输入的URI为/a/b/c,能否命中/a/*?
/a/b/c,能否命中/a/*?这个问题本质上是我们作为设计者,要考虑通配符*在路由中的语义,是只能代表1段路由,还是可以代表多段路由?
这里我们选择让通配符*只能表达1段路由.
我们假定通配符*表达多段路由.那么从框架使用者的视角上来看,路由/a/*和/a/*/*就很让人困惑:当路由为/a/b/c时,究竟匹配到了哪个路由?
从框架设计者的视角上来看,就干脆不要给使用者做出这种不良实践的机会.
Question2:若注册了2个路由:/user/123/home和/user/*/*,那么用户输入路径/user/123/detail时,能否命中路由/user/*/*?
/user/123/home和/user/*/*,那么用户输入路径/user/123/detail时,能否命中路由/user/*/*?这个问题本质上是我们作为设计者,要考虑我们的路由匹配过程是否可回溯?
可回溯的匹配过程如下图示:

简单理解就是:当发现/user/123/home不能匹配/user/123/detail时,要回溯到/user/*节点进行进一步查找的过程.
可以做,但这是一个投入大产出低的特性.
这里我们选择不支持这种路由,有2个原因:
可回溯的特性会使得你的代码变得非常复杂
从框架设计者的角度上来讲,通过不提供这种匹配方式的做法,限制框架的使用者不能定义形如
/user/*/*的路由./user/*/*不好的地方在于:当使用者A读使用者B的代码时,他一定是要非常清晰的了解一个前提:当路由/user/123/detail不能匹配到/user/123/home时,则能够匹配到/user/*/*.这样的代码可读性本身就不高,因此通过不提供支持回溯匹配过程的方式,限制使用者不要去写这种模糊的代码更进一步的,框架的使用者本身也不应该同时注册
/user/123/home和/user/*/*这两个路由
PART3. 实现通配符节点的创建
3.1 修改Node的结构
Node的结构node.go:
由于通配符节点的逻辑是需要特殊处理的(不管在注册路由还是查找路由时),因此需要为node结构体单独定义表示其通配符子节点的成员属性.
3.2 定义测试用例
此处还是在router_test.go中新建一个函数用于测试通配符匹配:
现在运行这个测试用例,结果是不符合预期的:
很明显,按我们现在的逻辑,/order/*中,通配符*成为了order节点的子节点,而我们预期中通配符*应该是order节点的通配符子节点(也就是wildcardChild字段值).


注:这里还是看他TDD的思路,是用测试用例告诉自己"哪里不符合预期".
很明显,在创建子节点的操作中出问题了.创建时不应该把*放在子节点集合里,而是应该放在wildcardChild字段上.
3.3 修改创建子节点的逻辑
node.go:
childOrCreate()方法中,添加了单独处理通配符子节点的逻辑.注意在childOrCreate()方法并不需要单独检测通配符子节点是否重复注册,因为其调用者router.addRoute()方法中有这个逻辑,不需要也不应该在childOrCreate()方法中检测通配符子节点是否重复注册.
这时候再去跑测试用例,实际上就已经通过了.
3.4 修改判断子节点相等的逻辑
在比对完两个节点的子节点映射数量之后,还要比对两个节点的通配符子节点是否相同.
router_test.go:
添加了比对两个节点的通配符子节点是否相等的逻辑
注:这里我将原来的形参y改名为target,为了提升可读性.
再跑单测也还是能跑的通的.
3.5 添加其他测试用例
3.5.1 根节点的通配符子节点
因为我们的实现中针对根节点有特殊逻辑,所以这里我们单独测试根节点的通配符子节点
router_test.go:
跑单测能够顺利通过.
3.5.2 通配符子节点的通配符子节点
router_test.go:
3.5.3 通配符子节点的普通子节点
router_test.go:
3.5.4 通配符子节点的普通子节点的通配符子节点
router_test.go:
Last updated