在 Go 的世界里, web 框架簡直多如牛毛.
前有自帶電池的標(biāo)準(zhǔn)庫(net/http), 后有簡潔優(yōu)雅的 Gin-Gonic(以下簡稱 Gin), 再有全棧開發(fā)一枝花 Beego, 等等不可勝數(shù).
使用 Go 有一段時(shí)間了, web 開發(fā)一直用的 Gin. Gin 的思想和 Python 框架 Flask 有頗多相似之處, 可以稱作 微框架 .
Gin 包括以下幾個(gè)主要的部分:
- 設(shè)計(jì)精巧的路由/中間件系統(tǒng);
- 簡單好用的核心上下文 Context;
- 附贈(zèng)工具集(JSON/XML 響應(yīng), 數(shù)據(jù)綁定與校驗(yàn)等).
本文意在探究 Gin 中間件的執(zhí)行原理. 我們先看如下的 Hello World 版 Gin 程序:
import (
"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)
}
}
這個(gè)簡單的 Gin 程序默認(rèn)啟用了兩個(gè)中間件, 分別是 Logger() 和 Recovery(), Context 是 Gin 的核心, 它的構(gòu)造如下:
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 沒有任何區(qū)別對吧, 我們怎么寫 HandlerFunc 就可以怎么寫一個(gè)中間件. 那么問題來了, 我們怎么解決一個(gè)請求和一個(gè)響應(yīng)經(jīng)過我們的中間件呢? 我們來寫個(gè)簡單的中間件分析一下:
r.Use(func(c *gin.Context) {
log.Println("Request in") // ①
c.Next() // next handler func
log.Println("Response out") // ②
})
神奇的語句出現(xiàn)了, 沒錯(cuò)就是 c.Next(), 所有中間件都有 Request 和 Response 的分水嶺, 就是這個(gè) c.Next(), 否則沒有辦法傳遞中間件. 我們來看源碼:
func (c *Context) Next() {
c.index++
s := int8(len(c.handlers))
for ; c.index < s; c.index++ {
c.handlers[c.index](c)
}
}
一個(gè)請求過來, Gin 會(huì)主動(dòng)調(diào)用 c.Next() 一次. 因?yàn)?handlers 是 slice , 所以后來者中間件會(huì)追加到尾部. 這樣就形成了形如 m1(m2(f())) 的調(diào)用鏈. 正如上面數(shù)字① ② 標(biāo)注的一樣, 我們會(huì)依次執(zhí)行如下的調(diào)用:
m1① -> m2① -> f -> m2② -> m1②
我們用下面一張圖來來總結(jié)這種關(guān)系:
本文來自:lingchao.xin
感謝作者:lingchao
