不能把所有路由塞进main.go,因其违背单一职责原则,导致文件臃肿难维护、测试复用困难、模块隔离失效;应按业务拆分路由组,由独立router文件注册,main.go仅负责启动与配置。

为什么不能把所有路由塞进 main.go
项目刚起步时,r.GET("/user", userHandler) 直接写在 main.go 里确实快。但一旦接口超过 20 个,文件就变成“路由迷宫”:找一个 /order/cancel 要 Ctrl+F 十几次,改个中间件得通读全文件,新人接手第一反应是删库跑路。更关键的是,main.go 本该只负责启动、配置和生命周期,混入业务路由会破坏单一职责,导致测试难、复用难、模块隔离失效。
用 router.Group() 按业务切分路由组
Gin 的 Group() 不只是加前缀,它是逻辑隔离单元。比如用户模块和订单模块,必须各自独立注册,互不感知:
// api/router/user.go
func RegisterUserRoutes(r *gin.RouterGroup) {
r.POST("/login", loginHandler)
r.GET("/profile", profileHandler)
r.PUT("/password", updatePasswordHandler)
}
// api/router/order.go
func RegisterOrderRoutes(r *gin.RouterGroup) {
r.POST("", createOrderHandler)
r.GET("/:id", getOrderHandler)
r.DELETE("/:id", cancelOrderHandler)
}
注意两点:
-
RegisterXXXRoutes函数接收*gin.RouterGroup,不是*gin.Engine,避免意外污染全局路由树 - 函数名统一用
RegisterXxxRoutes,Go 工具链能自动识别并提示未调用(比如go vet) - 不要在模块路由文件里调用
r.Use()—— 全局中间件统一在总入口注册,模块内中间件应封装成函数再传入
总路由初始化文件 router/router.go 怎么写
这个文件是唯一协调者,只做三件事:创建引擎、挂载各模块路由、设置全局中间件。它不该有业务逻辑,也不该 import handler 文件(handler 应由模块路由文件自己 import):
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
// router/router.go
func NewRouter() *gin.Engine {
r := gin.New()
// 全局中间件(日志、recover、CORS)
r.Use(gin.Logger(), gin.Recovery(), cors.Middleware())
// 按业务分组注册
v1 := r.Group("/api/v1")
{
user.RegisterUserRoutes(v1.Group("/users"))
order.RegisterOrderRoutes(v1.Group("/orders"))
product.RegisterProductRoutes(v1.Group("/products"))
}
return r
}
常见错误:
- 在
v1.Group("/users")里又嵌套.Group("admin")—— 这属于权限层级,应拆成独立模块(如adminuser.RegisterRoutes()),否则权限逻辑和业务耦合 - 把
gin.Default()用在总路由里 —— 它自带 Logger 和 Recovery,和你手动加的重复,且无法控制顺序;一律用gin.New()+ 显式Use() - 模块路由函数返回
*gin.Engine—— 这会让路由树失控,必须返回void,靠参数传入的*gin.RouterGroup修改状态
模块路由文件如何组织才不散乱
每个业务模块对应一对文件:api/controller/xxx.go(处理函数) + api/router/xxx.go(路由绑定)。目录结构必须严格:
api/
├── controller/
│ ├── user.go // 只放 userHandler, loginHandler 等
│ └── order.go
└── router/
├── user.go // 只 import controller/user.go,调用 RegisterUserRoutes
├── order.go
└── router.go // NewRouter 入口,只 import ./user, ./order 等,不 import controller
这样做的实际好处:
- 运行
go list ./api/router/...就能确认所有模块是否被正确 import,CI 可加检查 - 删掉
api/router/user.go,整个用户路由自动消失,无残留 - 测试某个模块路由时,可单独构造
gin.New().Group("/test")注册它,不依赖总路由
最易被忽略的一点:模块路由文件里不要出现 http.StatusOK 或 gin.H{} —— 这些属于 handler 层职责。路由文件只负责“谁处理哪个路径”,不关心返回什么。


















