golang gin 框架分组路由的原理

2021-03-28 15:33:15 +08:00
 liulei281

gin 文档中的一个例子

func main() {
	router := gin.Default()

	// Simple group: v1
	v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}

	// Simple group: v2
	v2 := router.Group("/v2")
	{
		v2.POST("/login", loginEndpoint)
		v2.POST("/submit", submitEndpoint)
		v2.POST("/read", readEndpoint)
	}

	router.Run(":8080")
}

router.Group方法源码是这样的:

// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
// For example, all the routes that use a common middleware for authorization could be grouped.
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
	return &RouterGroup{
		Handlers: group.combineHandlers(handlers),
		basePath: group.calculateAbsolutePath(relativePath),
		engine:   group.engine,
	}
}

从上面的例子看, v1 := router.Group("/v1")创建一个子分组v1, 并且v1.engine中存储了父级的信息, 这时候v1知道它的父级是router. 但是程序的入口是router, 但好像并没有把v1关联给router, 那么router怎么知道v1的存在?

(初学 golang, 请大佬解惑, 谢谢)

1867 次点击
所在节点    程序员
6 条回复
abersheeran
2021-03-28 15:41:33 +08:00
我纯 Golang 路人。但是 Gin 用的 Router 是基于 httprouter 的,httprouter 用的是 Radix Tree 。所以一定有一步是把节点加入树里。你再看看源码吧。
676529483
2021-03-28 15:55:16 +08:00
router 就是 Engine 对象,源码里面你看不是有 engine: group.engine 吗?
xxjwxc
2021-03-28 15:56:41 +08:00
catror
2021-03-28 16:09:20 +08:00
v1.POST() 和 v2.POST() 才是真正注册 route 的方法,group 只是方便理解和操作
cyjme
2021-03-28 16:16:56 +08:00
那么 router 怎么知道 v1 的存在?


=======

// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
// For example, all the routes that use a common middleware for authorization could be grouped.
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
return &RouterGroup{
Handlers: group.combineHandlers(handlers),
basePath: group.calculateAbsolutePath(relativePath),
engine: group.engine,
}
}

源码中把 /v1 用于了 basePath 。

然后从 router.GET() 这个方法开始看,看什么时候用到 basePath (也就是 /v1 )

// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle( http.MethodGet, relativePath, handlers) //进入
}


func (group *RouterGroup) handle( httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath) //进入
handlers = group.combineHandlers(handlers)
group.engine.addRoute( httpMethod, absolutePath, handlers)
return group.returnObj()
}

func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
return joinPaths(group.basePath, relativePath) //这里的 basePath 就是你的 "/v1",如果涉及到多重 group,可能是 /xxx/v1
}



=================
上面应该解答了你的疑问『怎么知道 v1 存在』,在每一次 GET/POST 执行的时候,都会根据之前的 basePath 进行计算。


继续深入,可以看 addRoute() ----> engine.trees 相关。然后就是 1 楼说的 httprouter tree 相关,如何快速查找匹配路由。
liulei281
2021-03-28 20:26:46 +08:00
@catror
@cyjme
谢谢, 明白怎么回事了.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/765887

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX