第04章:路由分组与路由组织
本章目标
学完本章后,你将能够:
- 使用
r.Group()创建路由分组 - 实现嵌套路由分组(
/api/v1/users) - 在分组上应用中间件
- 合理拆分路由文件,组织项目结构
- 遵循 RESTful 命名规范
前置知识
- 需要先学习:第03章(路由基础)
4.1 为什么需要路由分组
当你的 API 变多时,没有分组的路由会变成这样:
go
// ❌ 混乱:所有路由平铺,难以管理
r.GET("/api/v1/users", handler)
r.POST("/api/v1/users", handler)
r.GET("/api/v1/users/:id", handler)
r.PUT("/api/v1/users/:id", handler)
r.GET("/api/v1/orders", handler)
r.POST("/api/v1/orders", handler)
// ... 几十个路由,每次都要写 /api/v1/ 前缀用分组后:
go
// ✅ 清晰:分组管理,前缀只写一次
v1 := r.Group("/api/v1")
{
v1.GET("/users", handler)
v1.POST("/users", handler)
v1.GET("/users/:id", handler)
v1.PUT("/users/:id", handler)
v1.GET("/orders", handler)
v1.POST("/orders", handler)
}4.2 r.Group():创建分组
go
func main() {
r := gin.Default()
// 创建分组
api := r.Group("/api")
{
api.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "pong"})
})
api.GET("/status", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
}
// 实际路由:/api/ping 和 /api/status
r.Run(":8080")
}4.3 嵌套分组
go
r := gin.Default()
// 第一层:/api
api := r.Group("/api")
{
// 第二层:/api/v1
v1 := api.Group("/v1")
{
// 完整路径:/api/v1/users
v1.GET("/users", listUsers)
// 第三层嵌套
admin := v1.Group("/admin")
{
// 完整路径:/api/v1/admin/users
admin.GET("/users", adminListUsers)
}
}
// 第二层:/api/v2
v2 := api.Group("/v2")
{
// 完整路径:/api/v2/users
v2.GET("/users", listUsersV2)
}
}4.4 分组上应用中间件
中间件可以加在分组上,只对该分组生效:
go
r := gin.Default()
// 需要认证的分组
auth := r.Group("/api")
auth.Use(AuthMiddleware()) // 这个分组下的所有路由都需要认证
{
auth.GET("/profile", getProfile)
auth.PUT("/profile", updateProfile)
auth.GET("/orders", listOrders)
}
// 公开的分组(不需要认证)
public := r.Group("/api")
{
public.GET("/articles", listArticles)
public.GET("/articles/:id", getArticle)
}4.5 多文件路由组织
当路由变多时,应该把路由拆分到不同文件中:
推荐目录结构
gin-project/
├── main.go # 入口,组装路由
├── go.mod
├── config/
│ └── config.go # 配置
├── handlers/ # 处理函数(Controller)
│ ├── user.go
│ ├── order.go
│ └── article.go
├── middlewares/ # 中间件
│ ├── auth.go
│ └── logger.go
├── models/ # 数据模型
│ ├── user.go
│ └── order.go
└── routes/ # 路由注册
└── router.goroutes/router.go
go
package routes
import (
"gin-project/handlers"
"gin-project/middlewares"
"github.com/gin-gonic/gin"
)
func SetupRouter() *gin.Engine {
r := gin.Default()
// 公开路由
public := r.Group("/api")
{
public.POST("/login", handlers.Login)
public.POST("/register", handlers.Register)
}
// 需要认证的路由
protected := r.Group("/api")
protected.Use(middlewares.Auth())
{
// 用户
protected.GET("/users", handlers.ListUsers)
protected.GET("/users/:id", handlers.GetUser)
protected.PUT("/users/:id", handlers.UpdateUser)
protected.DELETE("/users/:id", handlers.DeleteUser)
// 订单
protected.GET("/orders", handlers.ListOrders)
protected.POST("/orders", handlers.CreateOrder)
}
return r
}main.go
go
package main
import "gin-project/routes"
func main() {
r := routes.SetupRouter()
r.Run(":8080")
}handlers/user.go
go
package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
)
func ListUsers(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"users": []string{"张三", "李四", "王五"},
})
}
func GetUser(c *gin.Context) {
id := c.Param("id")
c.JSON(http.StatusOK, gin.H{"id": id, "name": "张三"})
}
func UpdateUser(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "更新成功"})
}
func DeleteUser(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "删除成功"})
}4.6 路由命名规范
| 规范 | 示例 | 说明 |
|---|---|---|
| 资源用复数 | /users, /orders | 避免单复数混用 |
| 层级表示关系 | /users/:id/orders | 用户的订单 |
| 版本号前缀 | /api/v1/users | 版本控制 |
| 小写 + 连字符 | /user-profiles | 不用驼峰或下划线 |
| 查询参数过滤 | /users?status=active | 筛选用参数 |
| 避免动词 | ❌ /getUsers | 动词在 HTTP 方法里 |
常见错误
错误1:分组路径末尾多加了斜杠
go
// ❌ 分组路径末尾有 /
v1 := r.Group("/api/v1/")
// 注册路由时又加了 /
v1.GET("/users", handler)
// 实际路径:/api/v1//users (双斜杠!)错误2:所有代码堆在 main.go
go
// ❌ main.go 几千行,谁都看不懂
func main() {
r := gin.Default()
r.GET("/users", func(c *gin.Context) { ... }) // 100行
r.POST("/users", func(c *gin.Context) { ... }) // 100行
// ... 50个路由,5000行代码
}
// 拆分成 routes/ + handlers/ + models/错误3:忘记分组上的中间件会影响所有子路由
go
// auth 分组的中间件会影响下面所有路由
auth := r.Group("/api")
auth.Use(AuthMiddleware())
{
auth.GET("/profile", handler)
auth.POST("/logout", handler)
// 这两个接口都需要认证
}
// 如果 logout 不需要认证,应该放到别的分组本章小结
r.Group("/prefix")创建路由分组,避免重复写前缀- 嵌套分组实现
/api/v1/users这样的多层路径 group.Use(middleware)给分组加中间件- 推荐目录:
routes/+handlers/+middlewares/+models/ - RESTful 命名:复数、层级关系、版本前缀
练习题
- 创建一个项目,包含
/api/v1和/api/v2两个版本的路由分组。 - 在
/api/v1/admin分组上添加一个模拟的认证中间件。 - 将路由拆分成
routes/router.go和handlers/两个包。 - 设计一个博客系统的完整路由结构(文章、评论、用户),写出路由注册代码。
- (思考题)API 版本控制除了 URL 前缀(
/v1/),还有什么方式?各有什么优劣?