今天的任务是实现路由分组控制
关于分组控制
这里的分组是指对可以进行相似处理的路由进行分组,是否分为一个组通常是以前缀进行区分,例如 /admin 为一个分组,那么 /admin/a 和 /admin/b 就是该分组下的子分组。如果没有分组控制,那么就需要对这两个不同的路由分别进行控制,会增加不必要的工作,也会增加路由控制的难度和复杂度。
此外,分组支持嵌套,父分组的功能应该可以作用到子分组上,例如 /admin分组应该包含顶层分组 / 的相关处理。
数据结构
- 分组需要通过前缀来对分组进行区分,所以包含 string 类型的 prefix
- 分组之间要支持分组嵌套也就是需要知道当前分组的父辈,因此包含指向当前分组父亲分组的 RouterGroup 指针 parent
- 分组对象需要有访问Router路由对象的能力,因此在其中包含指向 Engine(整个框架所有资源由Engine统一协调) 的指针 engine
- 最后对已有的 Engine 对象进一步抽象,将其作为最顶层的分组,拥有RouterGroup的所有处理能力
最终 Engine 和 RouterGroup 的构造函数如下:
type (
RouterGroup struct { // 分组结构体
prefix string // 分组对应的前缀
parent *RouterGroup // 当前分组的父亲分组(支持分组嵌套)
engine *Engine // 所有分组共享同一个 Engine 实例(帮助group访问router)
}
Engine struct { // 实现 ServeHTTP的接口
*RouterGroup // 内嵌结构体,实现简单的继承,Engine可以继承RouterGroup的相关方法
router *router
groups []*RouterGroup // 存放所有分组
}
)
RouterGroup相关方法
修改与路由相关的各个函数,交由 RouterGroup 对象实现
- Engine 的构造函数,完成各个属性的初始化
func New() *Engine {
engine := &Engine{router: newRouter()}
engine.RouterGroup = &RouterGroup{engine: engine}
engine.groups = []*RouterGroup{engine.RouterGroup}
return engine
}
- 通过 Group 构造新的子 Group(但是新的子 Group 与当前 Group 共用同一个 engine)
func (group *RouterGroup) Group(prefix string) *RouterGroup {
engine := group.engine
newGroup := &RouterGroup{
prefix: group.prefix + prefix, // 分组前缀为父分组的前缀+当前的前缀
parent: group,
engine: engine, // 与父分组公用同一个 engine
}
engine.groups = append(engine.groups, newGroup)
return newGroup
}
- 通过 RouterGroup 实现之前 Engine 对应的相关路由函数
Engine中含内嵌结构体RouterGroup,继承了其所有属性和方法,所以我们既可以像之前一样通过engine添加路由,也可以通过routergroup添加路由。
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)
}
// ---添加GET请求
func (group *RouterGroup) GET(pattern string, handler HandlerFunc) {
group.addRoute("GET", pattern, handler)
}
// ---添加POST请求
func (group *RouterGroup) POST(pattern string, handler HandlerFunc) {
group.addRoute("POST", pattern, handler)
}
修改main.go中代码
func main() {
r := gee.New()
r.GET("/", func(c *gee.Context) {
c.HTML(http.StatusOK, "<h1>Hello Gee</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) {
// expect /hello?name=geektutu
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) {
// expect /hello/geektutu
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(":8090")
}