跳至主要内容

[gin] middleware 筆記

Next 與執行順序

middleware 執行的順序會從最前面的 middleware 開始執行,在 middleware function 中,一旦呼叫 Next() 方法後,就會往下一個 middleware 的 function 走,但這並不表示 Next() 後的內容不會被執行到,相反的,Next() 後面的內容會等到所有 middleware function 中 Next() 以前的程式碼都執行結束後,才開始執行,並且「由後往前」且「依序」完成(見輸出結果)

func main() {
router := gin.Default()
router.GET("/api", func(c *gin.Context) {
fmt.Println("First Middle Before Next")
c.Next()
fmt.Println("First Middle After Next")

}, func(c *gin.Context) {
fmt.Println("Second Middle Before Next")
c.Next()
fmt.Println("Second Middle After Next")

}, func(c *gin.Context) {

fmt.Println("Third Middle Before Next")
c.Next()
fmt.Println("Third Middle After Next")
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
}

執行的順序如下:

// Next 之前的內容會「由前往後」並且「依序」完成
First Middle Before Next
Second Middle Before Next
Third Middle Before Next

// Next 之後的內容會「由後往前」並且「依序」完成
Third Middle After Next
Second Middle After Next
First Middle After Next

使用 Abort, AbortWith 來跳過後面的 middleware

Exclude middleware from some routes:只在特定的路由中套用 middleware

在 Middleware 執行的過程中,如果想要在某個 middleware 中斷,不要繼續往後執行的話,可以使用 c.Abort()c.AbortWithStatus()c.AbortWithStatusJSON()c.AbortWithError() 語法來結束。但要留意的是,這個方法可以避免在它之後的 middleware 繼續被執行,但並不會影響已經執行的 middleware 的 Next()。舉例來說,我們在第二個 middleware 中呼叫了 c.AbortWithStatus()

router.GET("/api", func(c *gin.Context) {
fmt.Println("1 Middle Before Next")
c.Next()
fmt.Println("1 Middle After Next")
}, func(c *gin.Context) {
fmt.Println("2 Middle Before Next")

// 在第二個 middleware 執行 abort
c.AbortWithStatus(http.StatusNotModified)
c.Next()

fmt.Println("2 Middle After Next")
}, func(c *gin.Context) {
fmt.Println("3 Middle Before Next")
c.Next()
fmt.Println("3 Middle After Next")
}, func(c *gin.Context) {
fmt.Println("4 Middle Before Next")
c.Next()
fmt.Println("4 Middle After Next")
})

要留意的是,Abort() 要寫在 Next() 之前才有效果,如此,第 3 個和第 4 個 middleware 都不會被執行到,但第 1 個和第 2 個 middleware 的 Next() 前或後的程式碼依然都會被執行

輸出結果:

1 Middle Before Next
2 Middle Before Next
2 Middle After Next
1 Middle After Next

Customize Middleware

  • TokenAuthMiddleware

在 Middleware 中發送回應

如果想要在 middleware 直接回傳 response 不要再繼續後續的處理,可以使用 ctx.AbortWithStatus, ctx.AbortWithStatusJSON 等方法。

範例:Request ID middleware

建立一個 middleware 在所有的 response header 中加入 "X-Request-Id" 的項目。

對於不需要接收參數的 middleware 可以直接寫成 function:

// Credit: Gin middleware examples (https://sosedoff.com/2014/12/21/gin-middleware.html)
// 建立一個 middleware 在所有的 response header 中加入 "X-Request-Id" 的項目
func RequestIDMiddleware(ctx *gin.Context) {
uuidV4 := uuid.NewV4()
ctx.Header("X-Request-Id", uuidV4.String())

ctx.Next()
}

// 使用 middleware,直接帶入 function 即可
func main() {
router := gin.Default()
router.Use(RequestIDMiddleware)
}

如果 middleware 會需要接收參數的話,可以讓該 function 直接被執行後回傳另一個 middleware function:

func RequestIDMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
uuidV4 := uuid.NewV4()
ctx.Header("X-Request-Id", uuidV4.String())

ctx.Next()
}
}

// 使用 middleware,帶入 function 且「需執行」
func main() {
router := gin.Default()
router.Use(RequestIDMiddleware())
}