路由分组控制
目标
分组控制的意义
分组控制(Group Control)是 Web 框架应提供的基础功能之一。所谓分组,是指路由的分组。如果没有路由分组,我们需要针对每一个路由进行控制。
真实的业务场景中,往往某一组路由需要相似的处理。例如:
- 以
/post
开头的路由匿名可访问。
- 以
/admin
开头的路由需要鉴权。
- 以
/api
开头的路由是 RESTful 接口,可以对接第三方平台,需要三方平台鉴权。
大部分情况下的路由分组,是以相同的前缀来区分的。因此,我们今天实现的分组控制也是以前缀来区分,并且支持分组的嵌套。例如/post
是一个分组,/post/a
和/post/b
可以是该分组下的子分组。作用在/post
分组上的中间件(middleware),也都会作用在子分组,子分组还可以应用自己特有的中间件。
中间件可以给框架提供无限的扩展能力,应用在分组上,可以使得分组控制的收益更为明显,而不是共享相同的路由前缀这么简单。例如/admin
的分组,可以应用鉴权中间件;/
分组应用日志中间件,/
是默认的最顶层的分组,也就意味着给所有的路由,即整个框架增加了记录日志的能力。
分组嵌套
- 找到当前分组的父亲(parent)。
- 存储应用在该分组的中间件。
Group
Group的定义
在Group中,保存一个指针,指向Engine
,整个框架的所有资源都是由Engine
统一协调的,那么就可以通过Engine
间接地访问各种接口了。
1 2 3 4 5 6
| RouterGroup struct { prefix string middlewares []HandlerFunc parent *RouterGroup engine *Engine }
|
1 2 3 4 5
| Engine struct { *RouterGroup router *router groups []*RouterGroup }
|
实现RouterGroup
Engine
作为最顶层的分组,也就是说Engine
拥有RouterGroup
所有的能力。
Engine
从某种意义上继承了RouterGroup
的所有属性和方法,因为 (*Engine).engine 是指向自己的。这样实现,我们既可以像原来一样添加路由,也可以通过分组添加路由。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| func New() *Engine { engine := &Engine{router: newRouter()} engine.RouterGroup = &RouterGroup{engine: engine} engine.groups = []*RouterGroup{engine.RouterGroup} return engine }
func (group *RouterGroup) Group(prefix string) *RouterGroup { engine := group.engine newGroup := &RouterGroup{ prefix: group.prefix + prefix, parent: group, engine: engine, } engine.groups = append(engine.groups, newGroup) return newGroup }
func (group *RouterGroup) addRoute(method string, comp string, handler HandlerFunc) { pattern := group.prefix + comp log.Printf("Route %4s - %s", method, pattern) group.engine.router.addRoute(method, pattern, handler) }
func (group *RouterGroup) GET(pattern string, handler HandlerFunc) { group.addRoute("GET", pattern, handler) }
func (group *RouterGroup) POST(pattern string, handler HandlerFunc) { group.addRoute("POST", pattern, handler) }
|
调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| func main() { r := gee.New() r.GET("/index", func(c *gee.Context) { c.HTML(http.StatusOK, "<h1>Index Page</h1>") }) v1 := r.Group("/v1") { v1.GET("/", func(c *gee.Context) { c.HTML(http.StatusOK, "<h1>Hello Gee</h1>") })
v1.GET("/hello", func(c *gee.Context) { c.String(http.StatusOK, "hello %s, you're at %s\n", c.Query("name"), c.Path) }) } v2 := r.Group("/v2") { v2.GET("/hello/:name", func(c *gee.Context) { c.String(http.StatusOK, "hello %s, you're at %s\n", c.Param("name"), c.Path) }) v2.POST("/login", func(c *gee.Context) { c.JSON(http.StatusOK, gee.H{ "username": c.PostForm("username"), "password": c.PostForm("password"), }) })
}
r.Run(":9999") }
|