Gin 的 Group 是路由分组机制,用于自动拼接公共路径前缀并统一管理中间件;它返回新 RouterGroup 实例,继承父级中间件且可叠加自身中间件,不影响其他 group。

什么是 Gin 的 Group?它不是必须的,但能避免重复写前缀
Go Web 框架中,Gin 的 Group 是最常用也最直观的路由分组机制。它本身不改变底层路由匹配逻辑,只是帮你自动拼接公共路径前缀,并统一管理中间件。如果你的 API 有 /api/v1/users、/api/v1/posts、/api/v2/users 这类结构,不用 Group 就得每个 router.GET 都手动写完整路径,容易漏、难维护。
关键点:一个 Group 返回的是新的 *gin.RouterGroup 实例,它继承父 router 的中间件,也能叠加自己的中间件 —— 但不会影响其他 group。
apiV1 := router.Group("/api/v1")
{
apiV1.Use(AuthMiddleware()) // 只作用于该 group 下所有路由
apiV1.GET("/users", GetUsersHandler)
apiV1.POST("/users", CreateUserHandler)
}
apiV2 := router.Group("/api/v2")
{
apiV2.Use(NewAuthMiddleware()) // 独立中间件
apiV2.GET("/users", GetUsersV2Handler)
}
Group 嵌套是否可行?可以,但别嵌太深
你可以对 Group 再调用 Group,比如 admin := router.Group("/admin").Group("/users"),最终路径是 /admin/users。但实际开发中,三层及以上嵌套会让路由树难以追踪,尤其配合中间件时容易误判执行顺序。
- 嵌套 group 的中间件是“叠加”而非“覆盖”:外层中间件先执行,再进内层
- 调试时
router.Routes()输出的路径是拼接后的完整路径,不会显示嵌套层级 - 更推荐扁平化分组:按业务域(
/users)、版本(/v1)、权限(/admin)选一个主维度建 group,其余用子路径表达
如何让不同 group 共享中间件但又不污染全局?用 Use 而非 UseGlobal
router.UseGlobal() 不存在 —— Gin 没有这个方法。常见误解是以为 router.Use() 是全局的,其实它只作用于调用之后注册的路由(包括后续 Group),而 Group.Use() 只作用于该 group 内部。真正的“全局中间件”其实是 router.Use() 在 Group 创建之前调用:
立即学习“go语言免费学习笔记(深入)”;
router := gin.Default() router.Use(LoggerMiddleware(), RecoveryMiddleware()) // 所有路由都会经过apiV1 := router.Group("/api/v1") apiV1.Use(AuthMiddleware()) // 仅 /api/v1/* 经过 Auth
web := router.Group("/web") web.Use(SessionMiddleware()) // 仅 /web/* 经过 Session
注意:router.Use() 必须在任何 Group 或 GET/POST 注册前调用,否则对已注册的路由无效。
用 Any 或通配符做动态分组?不行,Gin 不支持运行时路径解析分组
有人想实现类似 router.Group("/:tenant"),然后在 group 内统一处理租户逻辑。这在 Gin 中无法直接做到 —— Group 只接受静态字符串前缀,不解析路径参数。你需要把租户识别提前到中间件里:
- 写一个中间件,在
c.Param("tenant")取值并存入c.Set("tenant_id", ...) - 在 handler 中用
c.MustGet("tenant_id")获取 - 或者用
router.Any("/:tenant/*path", TenantRouter)自定义分发逻辑
强行用 Group 做动态前缀会导致路由表爆炸(每个租户都注册一遍 group),且无法被 gin.BasicAuth() 等依赖静态路径的中间件兼容。
真正需要多租户路由时,分组只是组织手段,核心还是靠中间件提取上下文、handler 显式使用。











