V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liulei281
V2EX  ›  程序员

golang gin 框架分组路由的原理

  •  
  •   liulei281 · 2021-03-28 15:33:15 +08:00 · 1862 次点击
    这是一个创建于 1126 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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, 请大佬解惑, 谢谢)

    6 条回复    2021-03-28 20:26:46 +08:00
    abersheeran
        1
    abersheeran  
       2021-03-28 15:41:33 +08:00
    我纯 Golang 路人。但是 Gin 用的 Router 是基于 httprouter 的,httprouter 用的是 Radix Tree 。所以一定有一步是把节点加入树里。你再看看源码吧。
    676529483
        2
    676529483  
       2021-03-28 15:55:16 +08:00
    router 就是 Engine 对象,源码里面你看不是有 engine: group.engine 吗?
    xxjwxc
        3
    xxjwxc  
       2021-03-28 15:56:41 +08:00
    catror
        4
    catror  
       2021-03-28 16:09:20 +08:00
    v1.POST() 和 v2.POST() 才是真正注册 route 的方法,group 只是方便理解和操作
    cyjme
        5
    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
        6
    liulei281  
    OP
       2021-03-28 20:26:46 +08:00
    @catror
    @cyjme
    谢谢, 明白怎么回事了.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5330 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 07:26 · PVG 15:26 · LAX 00:26 · JFK 03:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.