- 实现路由分组控制Route Group Control
一、 分组的意义
1. 简要说明
- 分组控制Group Control是Web框架应提供的基础功能之一
- 所谓分组,是指路由的分组
- 如果没有路由分组,我们需要针对每一个路由进行控制,但在真实的业务场景中,往往某一组路由需要相似的处理,例如:
- 以/post开头的路由匿名可访问
- 以/admin开头的路由需要鉴权
- 以/api开头的路由是RESTful接口,可以对接第三方平台,需要三方平台鉴权
- 大部分情况下的路由分组,是以相同的前缀来区分的
- 中间件可以给框架提供无限的扩展能力,应用在分组上,可以使得分组控制的收益更为明显,而不是共享相同的路由前缀这么简单
- 一个Group对象需要具备哪些属性呢?首先是前缀prefix,比如/或者/api
- 要支持分组嵌套,需要知道当前分组的父亲parent是谁
- 中间件是应用在分组上的,还需要存储应用在该分组上的中间件middlewares
- Group对象,还需要有访问Router的能力,为了方便,我们可以在Group中,保存一个指针,指向Engine,整个框架的所有资源都是由Engine统一协调的,那么就可以通过Engine间接地访问各种接口了
2. 具体实现
- gee.go
// 将和路由有关的函数,都交给RouterGroup实现 type RouterGroup struct{ prefix string middlewares []HandlerFunc parent *RouterGroup engine *Engine } // 进一步抽象,将Engine作为最顶层的分组,也就是说Engine拥有RouterGroup所有的能力 // 整个框架的所有资源都是由Engine统一协调 // 通过Engine间接地访问各种接口 type Engine struct{ *RouterGroup router *router groups []*RouterGroup } 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 := &RouteGroup{ prefix: group.prefix + prefix, parent: group, engine: engine, } engine.groups = append(engine.groups, newGroup) return newGroup } // 调用了group.engine.router.addRoute来实现路由的映射 // 由于Engine从某种意义上继承了RouteGroup的所有属性和方法 // 这样既可以像之前一样添加路由,也可以通过分组添加路由 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) }
- main.go
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.Parma("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") }
- 测试
- curl “http://localhost:9999/v1/hello?name=geektutu”
- curl “http://localhost:9999/v2/hello/geektutu”
参考
- https://geektutu.com/post/gee-day4.html