深入理解Go gin框架中Context的Request和Writer對(duì)象
背景
在使用gin框架時(shí),我們定義的請(qǐng)求處理器,輸入?yún)?shù)總是一個(gè)gin.Context的指針類型,代表請(qǐng)求的上下文。在處理器的業(yè)務(wù)邏輯中,通過(guò)Context.Request可以獲取本次請(qǐng)求的參數(shù)值;通過(guò)Context.Writer就能將響應(yīng)結(jié)果輸出給客戶端了。如下代碼所示:
package main
import (
"github.com/gin-gonic/gin"
"log"
"net/http"
)
func main() {
r := gin.Default()
// 注冊(cè)路由,定義請(qǐng)求處理器函數(shù)
r.GET("/", func(c *gin.Context) {
c.Param
c.Writer.Write([]byte("Hello World"))
})
// 調(diào)用該函數(shù),則禁用日志帶顏色輸出
gin.DisableConsoleColor()
//使用該函數(shù),則強(qiáng)制日志帶顏色輸出,無(wú)論是在終端還是其他輸出設(shè)備
gin.ForceConsoleColor()
r.Run("127.0.0.1:8080")
}
那么,Context字段是在什么地方初始化的呢,為什么通過(guò)Context.Request字段就能讀取請(qǐng)求的參數(shù)呢,又為什么通過(guò)Context.Writer字段就能將響應(yīng)結(jié)果輸出給客戶端呢?接下來(lái)我們就一一解答這3個(gè)問(wèn)題。
Context對(duì)象的初始化
gin的Context對(duì)象實(shí)際上是在Engine對(duì)象的ServeHTTP函數(shù)中進(jìn)行初始化的,如下:
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
通過(guò)該函數(shù)我們可以看到,在第3行通過(guò)池化技術(shù)獲取到一個(gè)新的Context對(duì)象。獲取到該Context后,通過(guò)engine.handleHTTPRequest函數(shù)傳入Context參數(shù),進(jìn)而找到對(duì)應(yīng)的路由處理器,傳遞給具體的路由處理器。在上例中就是r.GET中的對(duì)應(yīng)處理器。
在這個(gè)函數(shù)中,還會(huì)把本次的請(qǐng)求體req賦值給Context.Request變量。這樣,在具體的請(qǐng)求處理器中就可以通過(guò)Context的各種方法從Request中獲取參數(shù)值了。比如GetHeader、GetRawData等。
我們?cè)賮?lái)回顧下go的http的啟動(dòng)和請(qǐng)求處理流程,以便了解ServeHTTP的調(diào)用時(shí)機(jī)。engine對(duì)象實(shí)際上是實(shí)現(xiàn)了go的標(biāo)準(zhǔn)包中的Handler接口。該接口中定義了ServeHTTP方法。在接收到請(qǐng)求后,net/http的包就會(huì)調(diào)用engine的ServeHTTP方法。如下圖中的engine.ServeHTTP部分:

Context.Request對(duì)象
在上節(jié)中,在Engine的ServeHTTP函數(shù)中,將函數(shù)的入?yún)eq賦值給了Context的Request對(duì)象。那么,這個(gè)req變量對(duì)象是從哪里來(lái)的呢?
Engine.ServeHTTP函數(shù)是在net/http/server.go文件的conn.serve函數(shù)中被調(diào)用的。conn代表本次請(qǐng)求的連接對(duì)象。conn.serve函數(shù)首先會(huì)從conn對(duì)象中讀取出本次的請(qǐng)求參數(shù)(包含請(qǐng)求頭等),并解析到request對(duì)象中,將該對(duì)象再賦值給Context中的Request字段。這樣,通過(guò)Context的Param、Form、Header等函數(shù),就可以從Request中讀取信息了。

Context.Writer對(duì)象
在具體的請(qǐng)求處理函數(shù)中,我們發(fā)現(xiàn)所有的輸出都是通過(guò)Context.Writer對(duì)象的Write函數(shù)將數(shù)據(jù)返回給客戶端的。那么,這里的Context.Writer對(duì)象是什么呢,為什么通過(guò)Context.Writer就是能結(jié)果返回給客戶端呢?
要回答這個(gè)問(wèn)題,我們還需要回到net/http/server.go文件的conn.serve函數(shù)中,response參數(shù)是在該函數(shù)中調(diào)用ServeHTTP時(shí)傳入的參數(shù),而該response參數(shù)是通過(guò)conn.readRequest函數(shù)返回的。

我們?cè)偻ㄟ^(guò)readRequest函數(shù)來(lái)看下response結(jié)構(gòu)體的關(guān)鍵字段,如下:

我們?cè)賮?lái)看Engine.ServeHTTP函數(shù): 首先,將response對(duì)象賦值給Context.writermem對(duì)象。即在c.writermem.reset(w)函數(shù)中執(zhí)行的。 其次,Context.Writer是對(duì)Context.writermem的引用。即Context.Writer依然是指向response對(duì)象。即在c.reset()函數(shù)中執(zhí)行的。

所以,在Context中跟response有關(guān)的關(guān)鍵字段如下:

最后,在業(yè)務(wù)邏輯中使用Context.Writer.Write函數(shù)輸出時(shí),實(shí)際上是執(zhí)行的response.Write函數(shù)。
我們?cè)賮?lái)看下response.Write函數(shù)的實(shí)現(xiàn):

寫入的時(shí)候是調(diào)用的response的w字段寫入的。那w字段又是什么呢?我們?cè)倩氐?code>server.go中的對(duì)response初始化的readRequest函數(shù)中,如下:

可以看到w是response的對(duì)象。同時(shí)將w還賦值給了w.cw.res。最后,w.w字段是基于w.cw的一個(gè)bufio.Writer對(duì)象。當(dāng)調(diào)用bufio.Writer的Flush時(shí),實(shí)際上是調(diào)用了bufio.Writer中的wr的Write方法。而wr的又是指向了chunkWriter對(duì)象的Write方法。最后該方法是執(zhí)行了w.conn.bufw.Write寫入了數(shù)據(jù)。
最終的寫入流程如下:

總結(jié)
本文深入分析了gin框架中Context中讀取請(qǐng)求中的數(shù)據(jù)以及寫入響應(yīng)數(shù)據(jù)的原理。本質(zhì)上Request的數(shù)據(jù)來(lái)源于conn對(duì)象。最終也是寫入到conn對(duì)象的bufw中。
以上就是深入理解Go gin框架中Context的Request和Writer對(duì)象的詳細(xì)內(nèi)容,更多關(guān)于Go gin框架Context的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決golang編譯提示dial tcp 172.217.160.113:443: con
這篇文章主要介紹了解決golang編譯提示dial tcp 172.217.160.113:443: connectex: A connection attempt failed,此問(wèn)題完美解決,需要的朋友可以參考下2023-02-02
Goland 關(guān)閉自動(dòng)移除未使用的包操作
這篇文章主要介紹了Goland 關(guān)閉自動(dòng)移除未使用的包操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
如何使用?Go?和?Excelize?構(gòu)建電子表格
這篇文章主要介紹了如何使用Go和Excelize構(gòu)建電子表格,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09

