Gin 中间件的配置方式有许多种,大致为:

  1. Handle()Any()Match()GET() 等基本路由方法中配置 HandlerFunc
  2. RouterGroupGroup() 方法中配置 HandlerFunc
  3. 使用 Use() 方法配置 HandlerFunc

特定请求的中间件

为特定请求配置中间件,可以直接在请求路由的 HandlerFunc 参数中配置。例如:

  • controllers/user

     1type userController struct{}
     2
     3var UserController userController
     4
     5func (userController) GetUser(ctx *gin.Context) {
     6  id := ctx.Param("id")
     7  // 获取 Context 内部字典的值
     8  token, exists := ctx.Get(middlewares.TOKEN)
     9  if !exists {
    10    ctx.JSON(http.StatusBadRequest, gin.H{"error": "Unauthorized..."})
    11  }
    12  ctx.PureJSON(http.StatusOK, gin.H{
    13    "id": id, "username": "zhangsan", "token": token,
    14  })
    15}
    
  • middlewares/user

     1type userMiddlewares struct{}
     2
     3var UserMiddlewares userMiddlewares
     4
     5const Authorization = "Authorization"
     6
     7func (userMiddlewares) VerifyToken(ctx *gin.Context) {
     8  auth := strings.TrimSpace(ctx.GetHeader(Authorization))
     9  if auth == "Bearer 123456" {
    10    // 使用 Context.Set() 设置键值对
    11    // 这个键值对只能在当前处理链中按顺序流转
    12    // 换句话说,这个键值对只是存储在当前 Context 中
    13    ctx.Set(TOKEN, "123456")
    14    ctx.Next()
    15  } else {
    16    ctx.JSON(http.StatusBadRequest, gin.H{"error": "Unauthorized..."})
    17    ctx.Abort()
    18  }
    19}
    

    中间件也可专门为其定义一个 middlewares 包来存放项目中所有的中间件。

    处理链:即一组按顺序执行的中间件和处理函数。

  • routers/user.go

    将中间函数和处理函数绑定到路由上:

    1func UserRoutersInit(engin *gin.Engine) {
    2	engin.Group("/user", middlewares.UserMiddleware).
    3		GET("/:id", middlewares.UserMiddlewares.VerifyToken,
    4			controllers.UserController.GetUser)
    5}
    

全局中间件

全局中间件就是绑定在根路由上的、全局生效的中间件,可以使用 RouterGroup.Use() 方法绑定全局中间件。例如:

1func Router() (engine *gin.Engine) {
2	engine = gin.Default()
3	// 设置全局中间件
4	engine.Use(middlewares.GlobalMiddlewares.VerifyToken)
5
6	// ...
7}

如果需要绑定的中间件过多,除了多次调用 User() 方法绑定或在 User() 方法中传入多个中间件外,还能使用 gin.HandlersChain 类型。gin.HandlersChain 的定义如下:

1type HandlersChain []HandlerFunc

使用 gin.HandlersChain 来聚集多个中间件,然后将它们一次性传入 User() 中:

 1var globalMiddlewares = gin.HandlersChain{
 2	middlewares.GlobalMiddlewares.VerifyToken,
 3	middlewares.GlobalMiddlewares.VerifyAuthorization,
 4 	// ...
 5}
 6
 7func Router() (engine *gin.Engine) {
 8  engine = gin.Default()
 9	// 设置全局中间件
10	engine.Use(globalMiddlewares...)
11
12	// ...
13}

通过使用 gin.HandlersChain 聚集并注册全局中间件后,中间函数的执行顺序将按照 gin.HandlersChain 中的顺序执行。


分组中间件

分组中间件同样可以使用 RouterGroup.Use() 方法,不同的是调用方法的对象是具体的路由分组对象,也就是 Engin.Group() 调用之后返回的对象。例如:

1engin.Group("/user").
2	Use(middlewares.UserMiddlewares.VerifyToken)

除了使用 RouterGroup.Use() 方法,分组中间件还可以在 Engin.Group() 调用的时候注册。例如:

1engin.Group("/user" middlewares.UserMiddlewares.VerifyToken)

同样地,也可以使用 gin.HandlersChain

1var groupMiddlewares = gin.HandlersChain{
2	middlewares.UserMiddlewares.VerifyToken,
3 	// ...
4}
5
6engin.Group("/user" ...groupMiddlewares)

注:User() 方法注册的中间件,和使用其它路由方法(如 Handle()Any()GET()Match() 等等)注册的中间件、处理函数它们之间的调用顺序也是按照注册的顺序执行。例如:

1engin.Group("/user", middlewares.UserMiddlewares.VerifyToken).
2	GET("/info/:username", controllers.UserController.GetInfo).
3	POST("/list", controllers.UserController.GetList).
4	// 在此之前的路由,在执行时不会调用 middlewares.UserMiddlewares.VerifyAuthorization
5	// 只会调用 middlewares.UserMiddlewares.VerifyToken
6	Use(middlewares.UserMiddlewares.VerifyAuthorization).
7	// 在此之前的路由,在执行时会按顺序调用 middlewares.UserMiddlewares.VerifyToken
8	// 和 middlewares.UserMiddlewares.VerifyAuthorization
9	GET("/:id", controllers.UserController.GetUser)

中间件 Context

在中间件中,Context 有一些方法可用于中间件的编写:

  • Context.Next():将控制权传递给下一个中间件或处理函数。

    1func MyMiddleware(c *gin.Context) {
    2	// 执行某些预处理逻辑
    3	c.Next()
    4	// 执行某些后处理逻辑
    5}
    
  • Context.Abort():立即停止执行后续的中间件和处理函数,可以用来处理错误或特殊请求。

    1func ErrorMiddleware(c *gin.Context) {
    2	if c.Query("error") == "true" {
    3		c.Abort()	// 停止执行后续的中间件和处理函数
    4	} else {
    5		c.Next()
    6	}
    7}
    
  • Context.Set()Context.Get():在当前请求处理链中存储和检索数据,允许在处理链中不同的中间件和处理函数之间共享信息。

     1func StoreDataMiddleware(c *gin.Context) {
     2	// 存储数据
     3	c.Set("user_id", 123)
     4	c.Next()
     5}
     6
     7func RetrieveDataMiddleware(c *gin.Context) {
     8	// 获取数据
     9	userId, err := c.Get("user_id")
    10	// 使用 userId ...
    11}
    

    Context.Set()Context.Get() 存储和获取的数据,是存储在当前 Context 对象的内部字典(store 字段)中。在 Gin 中,对于一条处理链中所有的中间件和处理函数共享同一个 Context 对象。

Contex 中除了上述的 4 个方法外,还有其它可以用于控制中间件或处理函数执行流的方法。例如以 Abort 开头的 Abort 系方法,它们在终止执行链执行的同时,还提供了其它一些额外的操作。比如说 Context.AbortWithStatus(),在中止执行链的同时设置响应状态码。