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


路由分组

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

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

RouterGroup 类型的定义如下:

type RouterGroup struct {
	Handlers HandlersChain
	basePath string
	engine   *Engine
	root     bool
}

其中:

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

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

// 创建路由组
user := r.Group("/user", middlewares.UserMiddleware)
{
  user.GET("", func(context *gin.Context) {
    context.String(http.StatusOK, "User get\n")
  })
  user.POST("", func(context *gin.Context) {
    context.String(http.StatusOK, "User post\n")
  })
  user.PUT("", func(context *gin.Context) {
    context.String(http.StatusOK, "User put\n")
  })
  user.DELETE("", func(context *gin.Context) {
    context.String(http.StatusOK, "User delete\n")
  })
}

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


Routers 包

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

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

func UserRoutersInit(engin *gin.Engine) {
  user := engin.Group("/user", middlewares.UserMiddleware)

  user.GET("", func(context *gin.Context) {
    context.String(http.StatusOK, "User get\n")
  })
  user.POST("", func(context *gin.Context) {
    context.String(http.StatusOK, "User post\n")
  })
  user.PUT("", func(context *gin.Context) {
    context.String(http.StatusOK, "User put\n")
  })
  user.DELETE("", func(context *gin.Context) {
    context.String(http.StatusOK, "User delete\n")
  })

  // 相对路径传入空串,表示请求路径与路由组的路径相同
  // 需要注意,不能使用 "/" 来表示 “当前请求路径与路由组的路径相同” 这一含义
}

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

func Router() (engine *gin.Engine) {
  // 创建根路由
	engine = gin.Default()

  // 初始化其它子路由
  UserRoutersInit(engine)
  // ...

	return
}

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

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

Controllers 包

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

// userController 类型
type userController struct{}
// UserController 示例
// 通过内部类型,以及定义外部实例,模拟一个简单的单例模式
var UserController userController

func (userController) GetUser(ctx *gin.Context) {
  context.String(http.StatusOK, "User get\n")
}

func (userController) AddUser(ctx *gin.Context) {
  context.String(http.StatusOK, "User post\n")
}

func (userController) UpdateUser(ctx *gin.Context) {
  context.String(http.StatusOK, "User put\n")
}

func (userController) DeleteUser(ctx *gin.Context) {
  context.String(http.StatusOK, "User delete\n")
}

接着,修改 routers/user.go

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