前面寫了兩篇與Gin
框架學習有關的文章,主要講了Gin
框架的安裝,定義處理HTTP
請求的各種方法以及如何根據客戶端需求返回不同格式的資料,但這中間漏了一個環節,那就是返回資料之前,如何獲取客戶端HTTP
請求中帶上來的引數,關於這點,我們就在這篇文章中講一講。
Gin
框架將處理HTTP
請求引數以及如何響應等操作都封裝到了gin.Conetxt
結構體,併為gin.Context
提供了非常多的方法,因此瞭解gin.Context
的結構定義與方法,對使用Gin
框架編寫Web
專案非常重要。
下面是gin.Context
結構定義程式碼:
type Context struct {
Request *http.Request
Writer ResponseWriter
Params Params
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]interface{}
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
Accepted []string
// contains filtered or unexported fields
}
複製程式碼
從上面的gin.Context
的結構定義來看,gin.Context
封裝了http.Request
和http.ResponseWriter
獲取請求引數
1. Path
path是指請求的url中域名之後從/開始的部分,如掘金首頁地址:https://juejin.im/timeline
,/timeline
部分便是path,可以使用gin.Context中的Param()方法獲取這部分引數。
func (c *Context) Param(key string) string
複製程式碼
使用Param()方法獲取path中的引數:
r.GET("/user/:id",func(c *gin.Context){
id := c.Param("id")
})
複製程式碼
除了使用gin.Context的中Param()方法外,還可以用gin.Context中的Params欄位獲取到path中的引數,Params的定義如下:
type Params []Param
func (ps Params) ByName(name string) (va string)
func (ps Params) Get(name string) (string, bool)
複製程式碼
使用gin.Context中的Params欄位獲取path中的引數示例如下:
r.GET("/user/:id",func(c *gin.Context){
id,err := c.Params.Get("id")
//id := c.Params.ByName("id")
})
複製程式碼
2. Query
query是指url請求地址中的問號後面的部,稱為查詢引數,如下面地址中,query=%E6%96%87%E7%AB%A0&type=all
就是查詢引數。
https://juejin.im/search?query=%E6%96%87%E7%AB%A0&type=all
複製程式碼
gin.Context
提供了以下幾個方法,用於獲取Query部分的引數。
1. 獲取單個引數
func (c *Context) GetQuery(key string) (string, bool)
func (c *Context) Query(key string) string
func (c *Context) DefaultQuery(key, defaultValue string) string
複製程式碼
上面三個方法用於獲取單個數值,GetQuery
比Query
多返回一個error型別的引數,實際上Query
方法只是封裝了GetQuery
方法,並忽略GetQuery
方法返回的錯誤而已,而DefaultQuery方法則在沒有獲取相應引數值的返回一個預設值。
示例如下:
r.GET("/user", func(c *gin.Context) {
id,_ := c.GetQuery("id")
//id := c.Query("id")
//id := c.DefaultQuery("id","10")
c.JSON(200,id)
})
複製程式碼
請求:http://localhost:8080/user?id=11
響應:11
2. 獲取陣列
GetQueryArray方法和QueryArray的區別與GetQuery和Query的相似。
func (c *Context) GetQueryArray(key string) ([]string, bool)
func (c *Context) QueryArray(key string) []string
複製程式碼
示例如下:
r.GET("/user", func(c *gin.Context) {
ids := c.QueryArray("id")
//id,_ := c.QueryArray("id")
c.JSON(200,ids)
})
複製程式碼
請求:http://localhost:8080/user?id=10&id=11&id=12
響應:["10","11","12"]
3. 獲取map
GetQueryArray方法和QueryArray的區別與GetQuery和Query的相似。
func (c *Context) QueryMap(key string) map[string]string
func (c *Context) GetQueryMap(key string) (map[string]string, bool)
複製程式碼
示例如下:
r.GET("/user", func(c *gin.Context) {
ids := c.QueryMap("ids")
//ids,_ := c.GetQueryMap("ids")
c.JSON(200,ids)
})
複製程式碼
請求:http://localhost:8080/user?ids[10]=zhang
響應:{"10":"zhang"}
3. Body
一般HTTP的Post請求引數都是通過body部分傳給伺服器端的,尤其是資料量大或安全性要求較高的資料,如登入功能中的賬號密碼等引數。
gin.Context提供了以下四個方法讓我們獲取body中的資料,不過要說明的是,下面的四個方法,只能獲取Content-type
是application/x-www-form-urlencoded
或multipart/form-data
時body
中的資料。
下面方法的使用方式與上面獲取Query的方法使用型別,區別只是資料來源不同而已,這裡便不再寫示例程式。
func (c *Context) PostForm(key string) string
func (c *Context) PostFormArray(key string) []string
func (c *Context) PostFormMap(key string) map[string]string
func (c *Context) DefaultPostForm(key, defaultValue string) string
func (c *Context) GetPostForm(key string) (string, bool)
func (c *Context) GetPostFormArray(key string) ([]string, bool)
func (c *Context) GetPostFormMap(key string) (map[string]string, bool)
func (c *Context) GetRawData() ([]byte, error)
複製程式碼
資料繫結
在前面的例子中,我們直接使用gin.Context
提供的方法獲取請求中通過path
、query
、body
帶上來的引數,但使用前面的那些方法,並不能處理請求中比較複雜的資料結構,比如Content-type為application/json或application/xml時,其所帶上的資料會很複雜,因此我們需要使用另外一種方法獲取這些資料,這種方式叫資料繫結
。
Gin
框架將資料繫結的操作都封裝在gin/binding這個包中,下面是gin/binding包
定義的常量,說明gin/binding
包所支援的Content-type
格式。
const (
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
MIMEYAML = "application/x-yaml"
)
複製程式碼
gin.binding
包也定義處理不同Content-type
提交資料的處理結構體,並以變數的形式讓其他包可以訪問,如下:
var (
JSON = jsonBinding{}
XML = xmlBinding{}
Form = formBinding{}
Query = queryBinding{}
FormPost = formPostBinding{}
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
MsgPack = msgpackBinding{}
YAML = yamlBinding{}
Uri = uriBinding{}
)
複製程式碼
但實際上,我們並不需要呼叫gin/binding
包的程式碼來完成資料繫結的功能,因為gin.Context
中已經在gin.Context
的基礎上封裝了許多更加快捷的方法供我們使用:
gin.Context
封裝的相關繫結方法,分為以Bind
為字首的系列方法和以ShouldBind
為字首的系列方法,這兩個系列方法之間的差別在於以Bind為字首的方法,在使用者輸入資料不符合相應格式時,會直接返回http狀態為400的響應給客戶端。
以Bind為字首的系列方法
1. Path
func (c *Context) BindUri(obj interface{}) error
複製程式碼
程式碼示例:
type User struct {
Uid int //使用者id
Username string //使用者名稱
}
func main() {
r := gin.Default()
r.GET("/bind/:uid/username", func(c *gin.Context) {
var u User
e := c.BindUri(&u)
if e == nil{
c.JSON(200,u)
}
})
r.Run()
}
複製程式碼
請求:http://localhost:8080/bind/1/小張
輸入:{1,"小張"}
2. Query
func (c *Context) BindQuery(obj interface{}) error
複製程式碼
程式碼示例:
r.GET("/bind/:uid/username", func(c *gin.Context) {
var u User
e := c.BindQuery(&u)
if e == nil{
c.JSON(200,u)
}
})
複製程式碼
請求:http://localhost:8080/bind?uid=1&username=小張
輸出:{1,"小張"}
3. Body
當我們在HTTP
請求中Body
設定不同資料格式,需要設定相應頭部Content-Type
的值,比較常用為json
、xml
、yaml
,gin.Context
提供下面三個方法繫結對應Content-type時body中的資料。
func (c *Context) BindJSON(obj interface{}) error
func (c *Context) BindXML(obj interface{}) error
func (c *Context) BindYAML(obj interface{}) error
複製程式碼
除了上面三個方法外,更常用的Bind()方法,Bind()方法會自動根據Content-Type的值選擇不同的繫結型別。
func (c *Context) Bind(obj interface{}) error
複製程式碼
示例
r.POST("bind",func(c *gin.Context){
u := User{}
c.Bind(&u)
})
複製程式碼
上面幾個方法都是獲取固定Content-type或自動根據Content-type選擇繫結型別,我們也可以使用下面兩個方法自行選擇繫結型別。
下面兩個方法的第二個引數值是gin.binding中定義好的常量,我們在上面講過。
func (c *Context) BindWith(obj interface{}, b binding.Binding) error
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error
複製程式碼
示例
r.POST("bind",func(c *gin.Context){
u := User{}
c.BindWith(&u,binding.JSON)
c.MustBindWith(&u,binding.JSON)
})
複製程式碼
以ShouldBind為字首的系列方法
以ShouldBind為字首的相應的方法與以Bind為字首的方法使用基本相同,因此下面沒有相應演示的程式碼。
1. Path
func (c *Context) ShouldBindUri(obj interface{}) error
複製程式碼
2. Query
func (c *Context) ShouldBindQuery(obj interface{}) error
複製程式碼
3. Body
func (c *Context) ShouldBind(obj interface{}) error
func (c *Context) ShouldBindJSON(obj interface{}) error
func (c *Context) ShouldBindXML(obj interface{}) error
func (c *Context) ShouldBindYAML(obj interface{}) error
func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error)
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error
複製程式碼
小結
Gin
框架在net/http
包的基礎上封裝了許多的方法,讓我們可以接收客戶端傳遞上來的各種不同格式的資料,但是從客戶端得到的資料之後,還是要驗證資料是否合法或是否我們想要的,這是Gin
框架中有關資料驗證器
的知識了,有機會再寫寫這方面的文章。