Gin中context的使用


Gin封裝的最好的地方就是context和對response的處理。本篇文章主要解釋context的使用方法, 以及其設計原理

將Request的處理封裝到Context中優點

在閱讀gin的源碼時, 請求的處理是使用type HandlerFunc func(*Context)來處理的. 也就是

func(context *gin.Context) {
    context.String(http.StatusOK, "some post")
}

參數是gin.Context, 但是查看源碼發現其實gin.Context在整個框架處理的地方只有下面這段

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)
}

為什么還要利用Context來處理呢?

gin的context實現了的context.Context Interface.經過查看context.Context相關資料, Context的最佳運用場景就是對Http的處理.。封裝成Conetxt另外的好處就是WithCancel, WithDeadline, WithTimeout, WithValue這些context包衍生的子Context就可以直接來使用.

gin.Context的設計

gin.Context主要由下面幾部分組成:

Metadata Management (我自己叫法:Key-Value)

這個模塊比較簡單, 就是從gin.Context中Set Key-Value, 以及各種個樣的Get方法, 如GetBoolGetString

實現這些功能也很簡單, 其實就是一個map

// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]interface{}

Input Data

這個模塊相當重要了, gin的README基本上都在介紹這個模塊的用法

Param ( 路由變量)

gin的標准叫法是Parameters in path. restful風格api如/user/john, 這個路由在gin里面是/user/:name, 要獲取john就需要使用Param函數

name := c.Param("name")

這個方法實現也很簡單, 就是在tree.go里面根據路由相關規則解析出來然后賦值給gin.ContextParams.

handlers, params, tsr := root.getValue(path, c.Params, unescape)

Query

/welcome?firstname=Jane&lastname=Doe這樣一個路由, first, last即是 Querystring parameters, 要獲取他們就需要使用 Query相關函數.
c.Query("first") // Jane
c.Query("last") // Doe

當然還有其他相關函數:

  • QueryMap
  • DefaultQuery 這個默認值的實現更加簡單, 當QueryString中不包含這個值, 直接返回填入的值

這些方法是的實現是利用net/httpRequest的方法實現的

PostForm

對於POSTPUT等這些能夠傳遞參數Body的請求, 要獲取其參數, 需要使用PostForm

POST /user/1

{
    "name":manu,
    "message":this_is_great
}
name := c.PostForm("name")
message := c.PostForm("message")

其他相關函數

  • DefaultPostForm

這些相關的方法是實現還是利用net/httpRequest的方法實現的

FormFile

對於文件相關的操作, 一般生產情況下不建議這樣使用, 因為把文件上傳到服務器磁盤, 還得磁盤相關的監控. 我覺得最好利用雲服務商相關的對象存儲, 如:阿里雲OSS, 七牛雲對象存儲, AWS的對象存儲等來做文件的相關操作.

 

Bind

內置的有json, xml, protobuf, form, query, yaml. 這些Bind極大的減少我們自己去解析各種個樣的數據格式, 提高我們的開發速度

Bind的實現都在gin/binding里面. 這些內置的Bind都實現了Binding接口, 主要是Bind()函數.

  • context.BindJSON() 支持MIME為application/json的解析
  • context.BindXML() 支持MIME為application/xml的解析
  • context.BindYAML() 支持MIME為application/x-yaml的解析
  • context.BindQuery() 只支持QueryString的解析, 和Query()函數一樣
  • context.BindUri() 只支持路由變量的解析
  • Context.Bind() 支持所有的類型的解析, 這個函數盡量還是少用(當QueryString, PostForm, 路由變量在一塊同時使用時會產生意想不到的效果), 目前測試Bind不支持路由變量的解析, Bind()函數的解析比較復雜, 這部分代碼后面再看

 

Response

對Header的支持

  • Header
  • GetHeader

這里的Header是寫到Response里面的Header. 對於客戶端發的請求的Header可以通過context.Request.Header.Get("Content-Type")獲取

Cookie

提供對session, cookie的支持

render

做api常用到的其實就是gin封裝的各種render. 目前支持的有:

  • func (c *Context) JSON(code int, obj interface{})
  • func (c *Context) Protobuf(code int, obj interface{})
  • func (c *Context) YAML(code int, obj interface{})
    ...

當然我們可以自定義渲染, 只要實現func (c *Context) Render(code int, r render.Render)即可.

這里我們常用的是一個方法是: gin.H{"error": 111}. 這個結構相當實用, 各種render都支持. 其實這個結構很簡單就是type H map[string]interface{}, 當我們要從map轉換各種各樣結構時, 不妨參考gin這里的代碼


 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM