day4-分组控制
Q7nl1s admin

这是用 go 写 Web 框架 Gee 的第四天。严冬已经过去。

本节主要实现:

  • 实现路由分组控制(Route Group Control),代码约50行

分组的意义

分组控制(Group Control)是 Web 框架提供的基础功能之一。所谓分组,是指路由的分组。如果没有路由分组,我们需要大亨对每一个路由进行控制。但是真实的业务场景当中,往往某一组路由需要相似的处理。例如:

  • /post 开头的路由匿名可访问。
  • /admin 开头的路由需要鉴权。
  • /api 开头的路由是 RESTful 接口,可以对接第三方平台,需要三方平台鉴权。

大部分情况下的路由分组,是以相同的前缀来区分的,英雌,我们今天实现的分组控制也是以前缀来区分的,并且支持分组的嵌套。例如 /post 是一个分组, /post/a/post/b 可以是该分组下的子分组。作用在 /post 分组上的中间件(middleware),也都会作用在子分组,子分组还可以应用自己特有的中间件。

中间件看可以给框架无限的扩展能力,应用在分组上,可以使分组控制的收益更为明显,而不是说共享的路由前缀这么简单。例如 /admin 的分组,可以应用鉴权中间件; / 分组应用日志中间件, / 是默认的最顶层的分组,也就意味着给所有的路由,即整个框架增加了记录日志的能力。

提供扩展能力支持中间件的内容,我们将在明天介绍。

##分组嵌套

一个 Group 对象需要具备哪些属性呢?首先是前缀(prefix),比如 / ,或者 /api ;要支持分组嵌套那么,需要知道的当前分二组的父亲是谁,当然还需要存储应用在该分组上的中间件(middlewares)。还记得,我们之前调用函数 (*Engine).addRoute() 来映射所有路由规则和 Handler 。如果 Group 对象需要直接映射路由规则的话,比如我们想在使用框架时,这么调用:

1
2
3
4
5
r := gee.New()
v1 := r.Group("/v1")
v1.GET("/", func(c *gee.Context) {
c.HTML(http.StatusOK, "<h1>Hello Gee</h1>")
})

那么 Group 对象,还需要有访问 Router 的能力,为了方便,我们可以在 Group 中,保存一个指针,指向 Engine ,整个框架的所有资源都是由 Engine 统一协调的,那么就可以通过 Engine 间接地访问各种接口了。

所以,最后的 Group 的定义是这样的:

day4-group/gee/gee.go

1
2
3
4
5
6
RouterGroup struct {
prefix string
middlewares []HandlerFunc // support middleware
parent *RouterGroup // support nesting
engine *Engine // all groups share a Engine instance
}

我们还可以进一步地抽象,将 Engine 作为最顶层的分组,也就是说 Engine 拥有 RouterGroup 所有的能力。

1
2
3
4
5
Engine struct {
*RouterGroup
router *router
groups []*RouterGroup // store all groups
}

那我们就可以将和路由有关的函数,都交给RouterGroup实现了。

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
// New is the constructor of gee.Engine
func New() *Engine{
engine := &Engine{router: newRouter()}
engine.RouteGroup = &RouterGroup{engine: engine}
engine.groups = []*RouterGroup{engine.RouterGroup}
return engine
}

// Group is defined to create a new RouterGroup
// remember all groups share the same Engine instance
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)
}

// GET defines the method to add GET request
func (group *RouterGroup) GET(pattern string, handler HandlerFunc) {
group.addRoute("GET", pattern, handler)
}

// POST defines the method to add POST request
func (group *RouterGroup) POST(pattern string, handler HandlerFunc) {
group.addRoute("POST", pattern, handler)
}

可以仔细观察下 addRoute 函数,调用了 group.engine.router.addRoute 来实现了路由的映射。由于 Engine 从某种意义上继承了 RouterGroup 的所有属性和方法,因为 (*Engine).engine 是指向自己的。这样实现,我们既可以像原来一样添加路由,也可以通过分组添加路由

使用 Demo

测试框架的Demo就可以这样写了:

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
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) {
// 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(":9999")
}

通过 postman 简单测试:

1
2
3
4
5
$ http://localhost:9999/v1/hello?name=wuxi
hello wuxi, you're at /v1/hello

$ http://localhost:9999/v2/hello/wuxi
hello wuxi, you're at /hello/wuxi

参考:

7days-golang/gee-web/day4-group at master · geektutu/7days-golang (github.com)

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View