[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())
}