Gin 中许多类型都可以认为它是一个路由(IRoutes)类型,这包括了 RouterGroupEngine等。并且 IRoutes 接口中定义的方法都会有一个 IRoutes 类型的返回值,使得 Gin Routers 可以支持链式调用,让代码更加简洁。


路由分组

RouterGroup 是一种路由组对象(Engine 继承自 RouterGroup,所以也可以认为是一个路由组)。通过 Group() 方法可以创建一个新的 RouterGroupGroup() 方法的定义如下:

1func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
2	return &RouterGroup{
3		Handlers: group.combineHandlers(handlers),
4		basePath: group.calculateAbsolutePath(relativePath),
5		engine:   group.engine,
6	}
7}
  • relativePath:路由组的相对路径。与 handle() 方法 相同,relativePath 同样需要使用 group.calculateAbsolutePath() 来计算出绝对路径。
  • handlers:路由组的处理方法,一般是将其作为中间处理函数(中间件)来定义。

RouterGroup 类型的定义如下:

1type RouterGroup struct {
2	Handlers HandlersChain
3	basePath string
4	engine   *Engine
5	root     bool
6}

其中:

  • 使用 rootengine 字段来标记当前路由组是否为根路由,以及根路由的地址(Engine 可以认为是当前 Gin 程序中所有路由组的根路由组);
  • Handlers 记录了当前路由组中所需要的一系列路由处理函数(其中也包括了它上层路由的处理函数);
  • basePath 指明了当前路由组的绝对路径。

路由组的使用方式如下所示:

 1// 创建路由组
 2user := r.Group("/user", middlewares.UserMiddleware)
 3{
 4  user.GET("", func(context *gin.Context) {
 5    context.String(http.StatusOK, "User get\n")
 6  })
 7  user.POST("", func(context *gin.Context) {
 8    context.String(http.StatusOK, "User post\n")
 9  })
10  user.PUT("", func(context *gin.Context) {
11    context.String(http.StatusOK, "User put\n")
12  })
13  user.DELETE("", func(context *gin.Context) {
14    context.String(http.StatusOK, "User delete\n")
15  })
16}

对于任何一个 RouterGroup 来说,可以继续使用 Group() 方法创建路由,即使该 RouterGroup 也是由其它 RouterGroup 创建的。


Routers 包

一般在项目中,会专门创建一个 routers 包来存放项目中路由配置的相关代码。

将一系列相关的路由抽取为理由组后,可以专门为其在 routers 包下创建一个文件来存放这一系列路由的配置。例如可以将 user 路由中的内容配置在 user.go 中:

 1func UserRoutersInit(engin *gin.Engine) {
 2  user := engin.Group("/user", middlewares.UserMiddleware)
 3
 4  user.GET("", func(context *gin.Context) {
 5    context.String(http.StatusOK, "User get\n")
 6  })
 7  user.POST("", func(context *gin.Context) {
 8    context.String(http.StatusOK, "User post\n")
 9  })
10  user.PUT("", func(context *gin.Context) {
11    context.String(http.StatusOK, "User put\n")
12  })
13  user.DELETE("", func(context *gin.Context) {
14    context.String(http.StatusOK, "User delete\n")
15  })
16
17  // 相对路径传入空串,表示请求路径与路由组的路径相同
18  // 需要注意,不能使用 "/" 来表示 “当前请求路径与路由组的路径相同” 这一含义
19}

routers 包中可以创建一个 routers.go 来配置根路由、设置全局中间件以及初始化其它路由:

 1func Router() (engine *gin.Engine) {
 2  // 创建根路由
 3  engine = gin.Default()
 4
 5  // 初始化其它子路由
 6  UserRoutersInit(engine)
 7  // ...
 8
 9  return
10}

根路由的创建除了使用 gin.Default() 外,还可以使用 gin.New()gin.Default() 默认使用了 gin.Logger()gin.Recovery()。使用 gin.New() 来创建路由,需要自己手动配置 LoggerRecovery 中间件:

1engine = gin.New()
2engine.Use(gin.Logger(), gin.Recovery())
  • gin.Logger():日志。
  • gin.Recovery():错误管理,它会 recover() 任何 panic(),并且返回给客户端 500 响应码。

Controllers 包

除了路由,路由处理器也可以定义在专门的 controllers 包中。例如,user 路由,首先在 controllers 包中创建 user.go

 1// userController 类型
 2type userController struct{}
 3// UserController 示例
 4// 通过内部类型,以及定义外部实例,模拟一个简单的单例模式
 5var UserController userController
 6
 7func (userController) GetUser(ctx *gin.Context) {
 8  context.String(http.StatusOK, "User get\n")
 9}
10
11func (userController) AddUser(ctx *gin.Context) {
12  context.String(http.StatusOK, "User post\n")
13}
14
15func (userController) UpdateUser(ctx *gin.Context) {
16  context.String(http.StatusOK, "User put\n")
17}
18
19func (userController) DeleteUser(ctx *gin.Context) {
20  context.String(http.StatusOK, "User delete\n")
21}

接着,修改 routers/user.go

1user.GET("", controllers.UserController.GetUser)
2user.POST("", controllers.UserController.AddUser)
3user.PUT("", controllers.UserController.UpdateUser)
4user.DELETE("", controllers.UserController.DeleteUser)