Gin Middleware

Middleware Gin 提供了友好的 middleware 接口,方便在 request 处理前后进行通用处理。

引用: Gin 中的 Context 包的 Next

使用示例

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	signature := ""

  // 最外层 middleware
	router.Use(func(c *gin.Context) {
    // handler 前
    signature += "A"
    fmt.Println("before:A", signature)

    // 挂起当前函数的执行流程,执行中间的 middleware 和 handler 处理
    c.Next()

    // handler 后
    signature += "A"
    fmt.Println("after:A", signature)
  })

  // 中层 middleware
  router.Use(func(c *gin.Context) {
    signature += "B"
    fmt.Println("before:B", signature)
	})

  // 注册 handler,最内层
	router.GET("/hello", func(c *gin.Context) {
    signature += "C"
		fmt.Println("GET:C", signature)
	})

	router.Run()
}
/*
before:A A
before:B AB
GET:C ABC
after:A ABCA
*/
  • middleware 可以设置多个,这里以两个举例。可以看出调用Use的顺序决定了 middleware 的层次关系, 就像一个洋葱一样,先调用Use加入的 middleware 包在洋葱的最外面、洋葱的中心是 handler。 程序的执行顺序就像用签子从中心扎穿洋葱的过程,preA->preB->hanlderC->postB->postA
  • 在程序执行到 middleware 中的流程时,可以用c.Next()/c.Abort()进行流程控制:

    • c.Next()表示”执行完剩下的(middleware+handler)再继续执行本流程”的post部分
    • c.Abort()表示”执行完本流程后,不再执行剩下的(middleware+handler)逻辑,直接返回最终结果”
      • c.Abort()后的本 middleware 的代码会正常执行完毕,不受影响
      • c.Abort()只有在c.Next()之前才起作用,其本质是通过控制责任链的 index 跳过所有后续节点
  • 另外,可以用c.IsAborted()查询当前Abort状态

底层实现

其实 middleware 的底层是 Chain of Responsibility Pattern,即把一系列 middleware 函数放在一个 hook 数组里逐个执行, 利用数组的 index 控制流程。

func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)
		c.index++
	}
}

const abortIndex int8 = math.MaxInt8 / 2
func (c *Context) Abort() {
	c.index = abortIndex
}