深入理解Go gin框架中Context的Request和Writer對象
背景
在使用gin框架時,我們定義的請求處理器,輸入?yún)?shù)總是一個gin.Context的指針類型,代表請求的上下文。在處理器的業(yè)務邏輯中,通過Context.Request可以獲取本次請求的參數(shù)值;通過Context.Writer就能將響應結果輸出給客戶端了。如下代碼所示:
package main import ( "github.com/gin-gonic/gin" "log" "net/http" ) func main() { r := gin.Default() // 注冊路由,定義請求處理器函數(shù) r.GET("/", func(c *gin.Context) { c.Param c.Writer.Write([]byte("Hello World")) }) // 調(diào)用該函數(shù),則禁用日志帶顏色輸出 gin.DisableConsoleColor() //使用該函數(shù),則強制日志帶顏色輸出,無論是在終端還是其他輸出設備 gin.ForceConsoleColor() r.Run("127.0.0.1:8080") }
那么,Context字段是在什么地方初始化的呢,為什么通過Context.Request字段就能讀取請求的參數(shù)呢,又為什么通過Context.Writer字段就能將響應結果輸出給客戶端呢?接下來我們就一一解答這3個問題。
Context對象的初始化
gin的Context對象實際上是在Engine對象的ServeHTTP函數(shù)中進行初始化的,如下:
// 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) }
通過該函數(shù)我們可以看到,在第3行通過池化技術獲取到一個新的Context
對象。獲取到該Context
后,通過engine.handleHTTPRequest函數(shù)傳入Context參數(shù),進而找到對應的路由處理器,傳遞給具體的路由處理器。在上例中就是r.GET中的對應處理器。
在這個函數(shù)中,還會把本次的請求體req
賦值給Context.Request
變量。這樣,在具體的請求處理器中就可以通過Context
的各種方法從Request
中獲取參數(shù)值了。比如GetHeader
、GetRawData
等。
我們再來回顧下go的http的啟動和請求處理流程,以便了解ServeHTTP的調(diào)用時機。engine對象實際上是實現(xiàn)了go的標準包中的Handler接口。該接口中定義了ServeHTTP方法。在接收到請求后,net/http的包就會調(diào)用engine的ServeHTTP方法。如下圖中的engine.ServeHTTP部分:
Context.Request對象
在上節(jié)中,在Engine的ServeHTTP函數(shù)中,將函數(shù)的入?yún)eq賦值給了Context的Request對象。那么,這個req變量對象是從哪里來的呢?
Engine.ServeHTTP
函數(shù)是在net/http/server.go
文件的conn.serve
函數(shù)中被調(diào)用的。conn
代表本次請求的連接對象。conn.serve
函數(shù)首先會從conn對象中讀取出本次的請求參數(shù)(包含請求頭等),并解析到request
對象中,將該對象再賦值給Context中的Request字段。這樣,通過Context的Param、Form、Header等函數(shù),就可以從Request中讀取信息了。
Context.Writer對象
在具體的請求處理函數(shù)中,我們發(fā)現(xiàn)所有的輸出都是通過Context.Writer
對象的Write
函數(shù)將數(shù)據(jù)返回給客戶端的。那么,這里的Context.Writer
對象是什么呢,為什么通過Context.Writer
就是能結果返回給客戶端呢?
要回答這個問題,我們還需要回到net/http/server.go
文件的conn.serve
函數(shù)中,response
參數(shù)是在該函數(shù)中調(diào)用ServeHTTP
時傳入的參數(shù),而該response
參數(shù)是通過conn.readRequest
函數(shù)返回的。
我們再通過readRequest函數(shù)來看下response結構體的關鍵字段,如下:
我們再來看Engine.ServeHTTP函數(shù): 首先,將response對象賦值給Context.writermem對象。即在c.writermem.reset(w)函數(shù)中執(zhí)行的。 其次,Context.Writer是對Context.writermem的引用。即Context.Writer依然是指向response對象。即在c.reset()函數(shù)中執(zhí)行的。
所以,在Context中跟response有關的關鍵字段如下:
最后,在業(yè)務邏輯中使用Context.Writer.Write函數(shù)輸出時,實際上是執(zhí)行的response.Write函數(shù)。
我們再來看下response.Write函數(shù)的實現(xiàn):
寫入的時候是調(diào)用的response
的w
字段寫入的。那w字段又是什么呢?我們再回到server.go
中的對response
初始化的readRequest
函數(shù)中,如下:
可以看到w
是response
的對象。同時將w還賦值給了w.cw.res
。最后,w.w
字段是基于w.cw
的一個bufio.Writer
對象。當調(diào)用bufio.Writer
的Flush
時,實際上是調(diào)用了bufio.Writer
中的wr
的Write
方法。而wr
的又是指向了chunkWriter
對象的Write
方法。最后該方法是執(zhí)行了w.conn.bufw.Write
寫入了數(shù)據(jù)。
最終的寫入流程如下:
總結
本文深入分析了gin框架中Context中讀取請求中的數(shù)據(jù)以及寫入響應數(shù)據(jù)的原理。本質(zhì)上Request的數(shù)據(jù)來源于conn對象。最終也是寫入到conn對象的bufw中。
以上就是深入理解Go gin框架中Context的Request和Writer對象的詳細內(nèi)容,更多關于Go gin框架Context的資料請關注腳本之家其它相關文章!
相關文章
解決golang編譯提示dial tcp 172.217.160.113:443: con
這篇文章主要介紹了解決golang編譯提示dial tcp 172.217.160.113:443: connectex: A connection attempt failed,此問題完美解決,需要的朋友可以參考下2023-02-02