深入理解Go gin框架中Context的Request和Writer對(duì)象
背景
在使用gin框架時(shí),我們定義的請(qǐng)求處理器,輸入?yún)?shù)總是一個(gè)gin.Context的指針類(lèi)型,代表請(qǐng)求的上下文。在處理器的業(yè)務(wù)邏輯中,通過(guò)Context.Request可以獲取本次請(qǐng)求的參數(shù)值;通過(guò)Context.Writer就能將響應(yīng)結(jié)果輸出給客戶(hù)端了。如下代碼所示:
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é)果輸出給客戶(hù)端呢?接下來(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ù)返回給客戶(hù)端的。那么,這里的Context.Writer
對(duì)象是什么呢,為什么通過(guò)Context.Writer
就是能結(jié)果返回給客戶(hù)端呢?
要回答這個(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):
寫(xiě)入的時(shí)候是調(diào)用的response
的w
字段寫(xiě)入的。那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
寫(xiě)入了數(shù)據(jù)。
最終的寫(xiě)入流程如下:
總結(jié)
本文深入分析了gin框架中Context中讀取請(qǐng)求中的數(shù)據(jù)以及寫(xiě)入響應(yīng)數(shù)據(jù)的原理。本質(zhì)上Request的數(shù)據(jù)來(lái)源于conn對(duì)象。最終也是寫(xiě)入到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-02Goland 關(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