Gin框架入门系列【6】中间件

413 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

中间件

中间件是Gin框架中比较重要的一个知识点,中间件在处理请求的过程中被执行,当一个请求加入中间件之后,那么每次对这个请求调用时中间件都会执行,而且在中间件的执行过程中还可以控制请求是继续执行还是直接返回,通常中间件会用在一些公共的地方,例如使用中间件做登录校验,如果有登录就继续执行后续逻辑,如果没有登录则直接返回。

中间件图例:

局部中间件

中间件可以分为全局中间件和局部中间件,局部中间件可以针对单个接口添加,而全局中间件则是针对所有请求都添加。

首先来了解局部中间件,在使用局部中间件之前先看一下在绑定路由时使用到的GET、POST等方法,从GET方法的签名func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc)可以看到除了绑定一个路径之外,还可以传入多个HandlerFunc,所以中间件就是通过在绑定路由时传入HandlerFunc来进行的。

代码示例:

func main() {
  g := gin.Default()

  g.GET("/index1", Middle(), func(c *gin.Context) {
    fmt.Println("处理请求")
    c.JSON(http.StatusOK, gin.H{
      "message": "ok1",
    })
  })

  err := g.Run(":8888")
  if err != nil {
    return
  }
}

func Middle() gin.HandlerFunc {
  return func(c *gin.Context) {
    fmt.Println("收到客户端请求")
    c.Next()
    fmt.Println("请求结果返回")
  }
}

在上面的代码中定义了一个中间件Middle(名字任意),该方法返回HandlerFunc类型,然后在index1这个路由上使用了这个中间件,所以当有请求index1时,Middle中间件就会执行。

输出示例:

从数据语句来看,中间件c.Next() 方法之前的语句是在具体的请求逻辑之前执行,而c.Next() 方法之后的语句则在具体的逻辑处理之后执行,这也就和前面的流程图一致。

在中间件中调用了Next方法,这个方法表示继续执行后面逻辑,与之对应的还有一个方法是Abort,表示不执行后面的具体逻辑,如果将上面的Next换成Abort将会有以下输出:

可以看到“请求结果返回”这个打印了但是具体逻辑里面的“处理请求”没有被打印,这就表明了Abort方法会终止当前请求直接返回,如果需要后面的“请求结果返回”也不执行可以在Abort方法后面加上return即可。(不可加到Abort方法之前,否则Abort方法不执行请求照样会进入到具体的逻辑处理中)

路由组

上面的代码都是针对单个路由的中间件,同样的也可以针对路由组添加中间件。

代码如下:

func main() {
  g := gin.Default()

  group := g.Group("/v1/api", Middle())
  group.GET("/index1", func(c *gin.Context) {
    fmt.Println("处理请求1")
    c.JSON(http.StatusOK, gin.H{
      "message": "ok1",
    })
  })

  group.GET("/index2", func(c *gin.Context) {
    fmt.Println("处理请求2")
    c.JSON(http.StatusOK, gin.H{
      "message": "ok2",
    })
  })

  err := g.Run(":8888")
  if err != nil {
    return
  }
}

func Middle() gin.HandlerFunc {
  return func(c *gin.Context) {
    fmt.Println("收到客户端请求")
    c.Next()
    fmt.Println("请求结果返回")
  }
}

上面代码中新建了一个路由组,然后这个路由组使用了中间件Middle,那么则表示所有该路由组下的路由在被调用时都会执行Middle这个中间件。

全局中间件

全局中间件就是所有的接口在被调用时都是执行指定的中间件。

代码如下:

func main() {
  g := gin.Default()

  g.Use(Middle())

  g.GET("/index1", func(c *gin.Context) {
    fmt.Println("处理请求1")
    c.JSON(http.StatusOK, gin.H{
      "message": "ok1",
    })
  })

  g.GET("/index2", func(c *gin.Context) {
    fmt.Println("处理请求2")
    c.JSON(http.StatusOK, gin.H{
      "message": "ok2",
    })
  })

  err := g.Run(":8888")
  if err != nil {
    return
  }
}

func Middle() gin.HandlerFunc {
  return func(c *gin.Context) {
    fmt.Println("收到客户端请求")
    c.Abort()
    fmt.Println("请求结果返回")
  }
}

全局中间件只需要使用路由的Use方法将中间件方法传入进去即可,这样就表示所有的接口在被调用时都会执行这个中间件。