学习资料来自:GitHub - geektutu/7days-golang: 7 days golang programs from scratch (web framework Gee, distributed cache GeeCache, object relational mapping ORM framework GeeORM, rpc framework GeeRPC etc) 7天用Go动手写/从零实现系列https://github.com/geektutu/7days-golang
Go语言动手写Web框架 - Gee第四天 分组控制Group | 极客兔兔 (geektutu.com)https://geektutu.com/post/gee-day4.html
这里的分组是指对可以进行相似处理的路由进行分组,是否分为一个组通常是以前缀进行区分,例如 /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 对象实现
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
}
engine.groups = append(engine.groups, newGroup)
return newGroup
}
添加路由:Engine中含内嵌结构体RouterGroup,继承了其所有属性和方法,所以我们既可以像之前一样通过engine添加路由,也可以通过routergroup添加路由。
根据不同的请求类型,调用 addRoute 实现相关路由的添加注册。
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)
}
分别实现以下几个路由的注册
1)构造新的 Engine对象,直接通过 engine 注册添加路由:
r := gee.New()
r.GET("/index", func(c *gee.Context) {
c.HTML(http.StatusOK, "Index Page
")
})
2) 构造分组v1,通过 routergroup 注册添加路由
v1 := r.Group("/v1")
{
v1.GET("/", func(c *gee.Context) {
c.HTML(http.StatusOK, "Hello Gee
")
})
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)
})
}
3) 构造分组v2,通过 routergroup 注册添加路由
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"),
})
})
}
使用curl命令简单测试,结果如下: