這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在 Go 的世界裡, web 架構簡直多如牛毛.
前有內建電池的標準庫(net/http), 後有簡潔優雅的 Gin-Gonic(以下簡稱 Gin), 再有全棧開發一枝花 Beego, 等等不可勝數.
使用 Go 有一段時間了, web 開發一直用的 Gin. Gin 的思想和 Python 架構 Flask 有頗多相似之處, 可以稱作 微架構 .
Gin 包括以下幾個主要的部分:
本文意在探究 Gin 中介軟體的執行原理.
我們先看如下的 Hello World 版 Gin 程式:
package mainimport ( "log" "net/http" "github.com/gin-gonic/gin")func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.String(http.StatusOK, "%s", "pong!") }) if err := r.Run("0.0.0.0:8080"); err != nil { log.Fatalln(err) }}
這個簡單的 Gin 程式預設啟用了兩個中介軟體, 分別是 Logger() 和 Recovery().
我們之前說過, Context 是 Gin 的核心, 它的構造如下:
type Context struct { writermem responseWriter Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 engine *Engine Keys map[string]interface{} Errors errorMsgs Accepted []string}
其中 handlers 我們通過源碼可以知道就是 []HandlerFunc. 而它的簽名正是:
type HandlerFunc func(*Context)
所以中介軟體和我們普通的 HandlerFunc 沒有任何區別對吧, 我們怎麼寫 HandlerFunc 就可以怎麼寫一個中介軟體.
那麼問題來了, 我們怎麼解決一個請求和一個響應經過我們的中介軟體呢?
我們來寫個簡單的中介軟體分析一下:
r.Use(func(c *gin.Context) { log.Println("Request in") // ① c.Next() // next handler func log.Println("Response out") // ② })
神奇的語句出現了, 沒錯就是 c.Next(), 所有中介軟體都有 Request 和 Response 的分水嶺, 就是這個 c.Next(), 否則沒有辦法傳遞中介軟體.
我們來看源碼:
func (c *Context) Next() { c.index++ s := int8(len(c.handlers)) for ; c.index < s; c.index++ { c.handlers[c.index](c) }}
一個請求過來, Gin 會主動調用 c.Next() 一次. 因為 handlers 是 slice , 所以後來者中介軟體會追加到尾部.
這樣就形成了形如 m1(m2(f())) 的調用鏈. 正如上面數字① ② 標註的一樣, 我們會依次執行如下的調用:
m1① -> m2① -> f -> m2② -> m1②
我們用下面一張圖來來總結這種關係: