4. 路由分组
...大约 2 分钟
1. 路由分组
分组控制(Group Control)是 Web 框架应提供的基础功能之一。
若不进行路由分组,那么就需要针对每一个路由分别进行设置和操作。
对路由进行分组的好处:
- 统一管理同一组的路由
 - 实现路由组嵌套,不同的子路由可以使用单独的中间件,也可以共享父路由的中间件
 
通过分组和中间件的组合,提高了系统的可扩展性。
2. 实现
https://github.com/dreamjz/golang-notes/tree/main/books/7-days-golang/Gee/day4-router-group
当前项目结构:
DAY4-ROUTER-GROUP
│  go.mod
│  go.work
│  main.go
│
└─gee
        context.go
        gee.go
        go.mod
        router.go
        router_group.go
        router_test.go
        trie.go
2.1 router_group
路由分组需要实现的功能:
- 添加子分组,通过当前分组创建子分组
 - 注册路由,可以为当前分组注册路由
 
路由分组的数据结构如下:
type RouterGroup struct {
	prefix      string        // 路由组前缀
	middlewares []HandlerFunc // 中间件
	parent      *RouterGroup  // 父母分组
	engine      *Engine       // 所有分组持有同一个 Engine 实例
}
prefix:当前路由的前缀middlewares:中间件函数列表parent:父母分组,用于分组嵌套engine:所有的分组持有同一个Engine实例,那么RouterGroup就具备Engine的所有功能,如注册路由
2.2 gee
所有的路由分组组成一个树形结构,所有的节点拥有一个指向其父母节点的指针。
Engine可以视作树的根节点,此时Engine与路由相关的功能就交给RouterGroup实现。
type Engine struct {
	*RouterGroup
	router *router
	groups []*RouterGroup // store all groups
}
groups:Engine实例存储所有的分组
2.3 router_group
package gee
import "log"
type RouterGroup struct {
	prefix      string        // 路由组前缀
	middlewares []HandlerFunc // 中间件
	parent      *RouterGroup  // 父母分组
	engine      *Engine       // 所有分组持有同一个 Engine 实例
}
// Group create a new RouterGroup
func (group *RouterGroup) Group(prefix string) *RouterGroup {
	engine := group.engine
	newGroup := &RouterGroup{
		prefix: group.prefix + prefix,
		parent: group,
		engine: engine, // All group share one Engine instance
	}
	// Add to group lists
	engine.groups = append(engine.groups, newGroup)
	return newGroup
}
func (group *RouterGroup) addRoute(method string, path string, handler HandlerFunc) {
	pattern := group.prefix + path
	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)
}
3. Demo
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>Welcome v1</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)
		})
	}
	r.Run(":8000")
}
$ curl http://localhost:8000/v1/hello?name=alice
Hello alice, you're at /v1/hello
$ curl http://localhost:8000/v2/
404 NOT FOUND: /v2/
$ curl http://localhost:8000/v2/hello/alice
Hello alice, you're at /v2/hello/alice
Reference
 Powered by  Waline  v2.15.2
