深入理解Go gin框架中Context的Request和Writer對象
背景
在使用gin框架時,我們定義的請求處理器,輸入?yún)?shù)總是一個gin.Context的指針類型,代表請求的上下文。在處理器的業(yè)務(wù)邏輯中,通過Context.Request可以獲取本次請求的參數(shù)值;通過Context.Writer就能將響應(yīng)結(jié)果輸出給客戶端了。如下代碼所示:
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ù),則強制日志帶顏色輸出,無論是在終端還是其他輸出設(shè)備
gin.ForceConsoleColor()
r.Run("127.0.0.1:8080")
}
那么,Context字段是在什么地方初始化的呢,為什么通過Context.Request字段就能讀取請求的參數(shù)呢,又為什么通過Context.Writer字段就能將響應(yīng)結(jié)果輸出給客戶端呢?接下來我們就一一解答這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行通過池化技術(shù)獲取到一個新的Context對象。獲取到該Context后,通過engine.handleHTTPRequest函數(shù)傳入Context參數(shù),進而找到對應(yīng)的路由處理器,傳遞給具體的路由處理器。在上例中就是r.GET中的對應(yīng)處理器。
在這個函數(shù)中,還會把本次的請求體req賦值給Context.Request變量。這樣,在具體的請求處理器中就可以通過Context的各種方法從Request中獲取參數(shù)值了。比如GetHeader、GetRawData等。
我們再來回顧下go的http的啟動和請求處理流程,以便了解ServeHTTP的調(diào)用時機。engine對象實際上是實現(xiàn)了go的標(biāo)準(zhǔn)包中的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就是能結(jié)果返回給客戶端呢?
要回答這個問題,我們還需要回到net/http/server.go文件的conn.serve函數(shù)中,response參數(shù)是在該函數(shù)中調(diào)用ServeHTTP時傳入的參數(shù),而該response參數(shù)是通過conn.readRequest函數(shù)返回的。

我們再通過readRequest函數(shù)來看下response結(jié)構(gòu)體的關(guān)鍵字段,如下:

我們再來看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有關(guān)的關(guān)鍵字段如下:

最后,在業(yè)務(wù)邏輯中使用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對象。當(dāng)調(diào)用bufio.Writer的Flush時,實際上是調(diào)用了bufio.Writer中的wr的Write方法。而wr的又是指向了chunkWriter對象的Write方法。最后該方法是執(zhí)行了w.conn.bufw.Write寫入了數(shù)據(jù)。
最終的寫入流程如下:

總結(jié)
本文深入分析了gin框架中Context中讀取請求中的數(shù)據(jù)以及寫入響應(yīng)數(shù)據(jù)的原理。本質(zhì)上Request的數(shù)據(jù)來源于conn對象。最終也是寫入到conn對象的bufw中。
以上就是深入理解Go gin框架中Context的Request和Writer對象的詳細內(nèi)容,更多關(guān)于Go gin框架Context的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
gin框架Context如何獲取Get?Query?Param函數(shù)數(shù)據(jù)
這篇文章主要為大家介紹了gin框架Context?Get?Query?Param函數(shù)獲取數(shù)據(jù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Golang 統(tǒng)計字符串字數(shù)的方法示例
本篇文章主要介紹了Golang 統(tǒng)計字符串字數(shù)的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
Go語言基礎(chǔ)結(jié)構(gòu)體用法及示例詳解
這篇文章主要為大家介紹了Go語言基礎(chǔ)結(jié)構(gòu)體的用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2021-11-11
windows安裝部署go超詳細實戰(zhàn)記錄(實測有用!)
Golang語言在近年來因為其高性能、編譯速度快、開發(fā)成本低等特點逐漸得到大家的青睞,這篇文章主要給大家介紹了關(guān)于windows安裝部署go超詳細實戰(zhàn)的相關(guān)資料,需要的朋友可以參考下2023-02-02
Golang Map value不可尋址使用指針類型代替示例詳解
這篇文章主要為大家介紹了Golang Map value不可尋址使用指針類型代替示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11

