日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Gin 框架学习笔记(02)— 参数自动绑定到结构体

發布時間:2023/11/28 生活经验 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Gin 框架学习笔记(02)— 参数自动绑定到结构体 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

參數綁定模型可以將請求體自動綁定到結構體中,目前支持綁定的請求類型有 JSONXML 、YAML 和標準表單 form數據 foo=bar&boo=baz 等。換句話說,只要定義好結構體,就可以將請求中包含的數據自動接收過來,這是 Gin 框架非常神奇的功能。
?

在定義綁定對應的結構體時,需要給結構體字段設置綁定類型的標簽,比如綁定 JSON 數據時,設置字段標簽為 json:"fieldname" 。使用綁定可以更快捷地把數據傳遞給程序中的數據對象。
?

使用 Gin框架中系列綁定有關方法時,Gin 會根據請求頭中 Content-Type 推斷如何綁定,也就是自動綁定。但如果明確綁定的類型,開發人員也可以使用 MustBindWith() 方法或 BindJSON() 等方法而不用自動推斷。可以指定結構體某字段是必需的,字段需要設置標簽 binding:"required" ,但如果綁定時是空值,Gin 會報錯。
?

Gin 框架的 binding 包中,定義了 Content-Type 請求頭信息的多種 MIME 類型,以便在自動綁定時進行類型判別進而采用對應的處理方法:

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

在所有綁定的方法中,首先 c.Bind() 方法會根據 Content-Type 推斷得到一個 bindding 實例對象。因為它會調用函數 func Default(method, contentType string) Binding ,這個函數根據 HTTP 請求的方法和 Content-Type 來實例化具體的 bindding 對象。一共可以實例化為下面幾種類型:

var (JSON          = jsonBinding{}XML           = xmlBinding{}Form          = formBinding{}Query         = queryBinding{}FormPost      = formPostBinding{}FormMultipart = formMultipartBinding{}ProtoBuf      = protobufBinding{}MsgPack       = msgpackBinding{}YAML          = yamlBinding{}Uri           = uriBinding{}Header        = headerBinding{}
)

binding 包也就是 binding 目錄中,可以看到每種實例結構都單獨在一個文件定義了系列處理方法。 c.Bind() 方法得到 binding 實例對象后,會調用 c.MustBindWith(obj, b) 方法, b 為實例化的某類 binding 對象,而像 c.BindJSON() 方法由于知道實例化對象是 JSON ,所以也調用 c.MustBindWith(obj, b) ,這里的 bjsonBinding{} 對象。其他像 XML 等的處理過程類似。
?

c.MustBindWith() 方法會統一調用 c.ShouldBindWith() 方法,在 c.ShouldBindWith() 方法中會調用具體實例的處理方法: b.Bind(c.Request, obj) ,這個 b.Bind()方法很關鍵,每種 binding 實例對象都有實現這個方法,它實現了參數的綁定功能。
?

在參數綁定過程中,大致可以認為是這個過程:

Bind->MustBindWith->ShouldBindWith->b.Bind

在參數綁定中,無論是采用 c.Bind() 系列方法、或者是 c.ShouldBindWith() 系列方法,最終都是通過具體實例的 b.Bind() 方法來實現參數綁定到結構體指針。而這個實例可以在 binding 目錄中找到其方法的實現文件。如: json.gouri.go 以及 form.go 等等文件,文件名都對應著不同的 Content-Type 。
?

Gin 框架中下列方法可以用處理綁定:

// Bind 檢查 Content-Type 來自動選擇綁定引擎
// 依靠 "Content-Type" 頭來使用不同的綁定
//     "application/json" 綁定 JSON
//     "application/xml"  綁定 XML
// 否則返回錯誤信息
// 如果 Content-Type ==“application / json”,JSON 或 XML 作為 JSON 輸入,
// Bind 會將請求的主體解析為 JSON。
// 它將 JSON 有效負載解碼為指定為指針的結構。
// 如果輸入無效,它會寫入 400 錯誤并在響應中設置 Content-Type 標題 “text / plain” 。
func (c *Context) Bind(obj interface{}) error// BindJSON 是 c.MustBindWith(obj, binding.JSON) 的簡寫
func (c *Context) BindJSON(obj interface{}) error// BindXML 是 c.MustBindWith(obj, binding.BindXML) 的簡寫
func (c *Context) BindXML(obj interface{}) error// BindQuery 是 c.MustBindWith(obj, binding.Query) 的簡寫
func (c *Context) BindQuery(obj interface{}) error// BindYAML 是 c.MustBindWith(obj, binding.YAML) 的簡寫
func (c *Context) BindYAML(obj interface{}) error// BindHeader 是 c.MustBindWith(obj, binding.Header) 的簡寫
func (c *Context) BindHeader(obj interface{}) error// BindUri 使用 binding.Uri 綁定傳遞的結構體指針。
// 如果發生任何錯誤,它將使用 HTTP 400 中止請求。
func (c *Context) BindUri(obj interface{}) error// MustBindWith 使用指定的綁定引擎綁定傳遞的 struct 指針。
// 如果發生任何錯誤,它將使用 HTTP 400 中止請求。
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error// ShouldBind 檢查 Content-Type 來自動選擇綁定引擎
// 依靠 "Content-Type" 頭來使用不同的綁定
//     "application/json" 綁定 JSON
//     "application/xml"  綁定 XML
// 否則返回錯誤信息
// 如果 Content-Type ==“application/json” ,JSON 或 XML 作為 JSON 輸入,
// Bind 會將請求的主體解析為JSON。
// 它將 JSON 有效負載解碼為指定為指針的結構。
// 類似 c.Bind() ,但這個方法在 JSON 無效時不支持寫 400 到響應里。
func (c *Context) ShouldBind(obj interface{}) error// ShouldBindJSON  是c.ShouldBindWith(obj, binding.JSON)的簡寫
func (c *Context) ShouldBindJSON(obj interface{}) error// ShouldBindXML  是c.ShouldBindWith(obj, binding.XML)的簡寫
func (c *Context) ShouldBindXML(obj interface{}) error// ShouldBindQuery  是c.ShouldBindWith(obj, binding.Query)的簡寫
func (c *Context) ShouldBindQuery(obj interface{}) error// ShouldBindYAML  是c.ShouldBindWith(obj, binding.YAML)的簡寫
func (c *Context) ShouldBindYAML(obj interface{}) error// ShouldBindHeader  是c.ShouldBindWith(obj, binding.Header)的簡寫
func (c *Context) ShouldBindHeader(obj interface{}) error// ShouldBindUri使用指定的綁定引擎綁定傳遞的struct指針。
func (c *Context) ShouldBindUri(obj interface{}) error// ShouldBindWith使用自定的綁定引擎綁定傳遞的struct指針。
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error// ShouldBindBodyWith與ShouldBindWith類似,但它存儲請求
// ShouldBindBodyWith可進入上下文,并在再次調用時重用。
//
// 注意:此方法在綁定之前讀取正文。 所以推薦使用
// 如果只需要調用一次,那么ShouldBindWith可以獲得更好的性能。
func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error)

1. 綁定查詢字符串或表單數據

表單和 URLQuery 方式傳遞參數,程序通過綁定的方式得到參數值,在參數的提取上更加自動。

package mainimport ("log""github.com/gin-gonic/gin"
)type Person struct {Name     string    `form:"name"`Address  string    `form:"address"`
}func main() {route := gin.Default()route.POST("/testing", startPage)route.Run(":8080")
}func startPage(c *gin.Context) {var person Person// 如果是 `GET` 請求,只使用 `Form` 綁定引擎(`query`)。// 如果是 `POST` 請求,首先檢查 `content-type` 是否為 `JSON` 或 `XML`,// 然后再使用 `Form`(`form-data`)。if c.ShouldBind(&person) == nil {log.Println(person.Name)log.Println(person.Address)}c.String(200, "Success")
}

程序運行在 Debug 模式時,在命令行運行下面三條命令:

curl -X POST  "http://localhost:8080/testing?name=appleboy&address=xyz"curl -H "Content-Type:application/json"  -X POST -d '{"name":"appleeboy","address":"xyz"}' <http://localhost:8080/testing>curl -H "Content-Type:application/x-www-form-urlencoded" -X POST -d "name=appleboy&address=xyz" "<http://localhost:8080/testing>"

輸出結果:

[GIN-debug] Listening and serving HTTP on :8080
2019/07/13 12:54:34 appleboy
2019/07/13 12:54:34 xyz
[GIN] 2019/07/13 - 12:54:34 | 200 | 18.9504ms | 127.0.0.1 | POST /testing?name=appleboy&address=xyz
2019/07/13 12:54:38 appleeboy
2019/07/13 12:54:38 xyz
[GIN] 2019/07/13 - 12:54:38 | 200 | 0s | 127.0.0.1 | POST /testing
2019/07/13 12:54:46 appleboy
2019/07/13 12:54:46 xyz
[GIN] 2019/07/13 - 12:54:46 | 200 | 0s | 127.0.0.1 | POST /testing

通過 POST 方法,采用 Urlencoded 編碼或 JSON 方式都能被綁定正常解析。但如果把程序接收方法改為 GET 方法:

route.GET("/testing", startPage)

則只能通過 URL Query 傳遞參數:

curl -X GET "http://localhost:8080/testing?name=appleboy&address=xyz"

這樣通過 URL Query 傳遞參數也能被正常綁定。
?

2. Multipart/Urlencoded 綁定

通過表單傳遞參數,下面程序通過綁定的方式得到參數值。

type LoginForm struct {User     string `form:"user" binding:"required"`Password string `form:"password" binding:"required"`
}func main() {router := gin.Default()router.POST("/login", func(c *gin.Context) {var form LoginForm// 可顯式綁定表單// c.ShouldBindWith(&form, binding.Form)// 或者簡單地使用 ShouldBind 方法自動綁定if c.ShouldBind(&form) == nil {if form.User == "user" && form.Password == "password" {c.JSON(200, gin.H{"status": "you are logged in"})} else {c.JSON(401, gin.H{"status": "unauthorized"})}}})router.Run(":8080")
}

上面程序中結構體的標簽: form:"user" ,表示在 form 表單中的名為 user

User     string `form:"user" binding:"required"`
Password string `form:"password" binding:"required"`

程序運行在 Debug 模式時,在命令行運行下面兩條命令:

curl -X POST  -d "user=user&password=password" <http://localhost:8080/login>Curl -H "Content-Type:multipart/form-data" -X POST -d "user=user&password=password" http://localhost:8080/login

3. URI 參數綁定

Gin 框架支持在路由 URI 中存在參數,也支持通過綁定得到這些參數,需要在結構體中指定字段標簽為 uri。

package mainimport "github.com/gin-gonic/gin"type Person struct {ID string `uri:"id" binding:"required,uuid"`Name string `uri:"name" binding:"required"`
}func main() {route := gin.Default()route.GET("/:name/:id", func(c *gin.Context) {var person Personif err := c.ShouldBindUri(&person); err != nil {c.JSON(400, gin.H{"msg": err})return}c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})})route.Run(":8088")
}

上面程序中結構體的標簽: uri:"id" ,表示在 URI 中的參數名為 id

UserID  string `uri:"id" binding:"required"`
Name   string `uri:"name" binding:"required"`

程序運行在 Debug 模式時,在命令行運行下面命令:

curl  -X GET http://localhost:8080/Go/42

4. 綁定 HTML 復選框

Gin 框架很方便地通過綁定得到 HTML FORM 元素的值,需要在結構體中指定字段標簽form:filedname 。

type CheckForm struct {Colors []string `form:"colors[]"`
}func main() {router := gin.Default()router.Static("/", "./public")router.POST("/check", func(c *gin.Context) {var form CheckForm// 簡單地使用 ShouldBind 方法自動綁定if c.ShouldBind(&form) == nil {c.JSON(200, gin.H{"color": form.Colors})}})router.Run(":8080")
}

index.html 文件放在程序目錄下 public 目錄中。

<form action="/check" method="POST"><p>Check some colors</p><label for="red">Red</label><input type="checkbox" name="colors[]" value="red" id="red"><label for="green">Green</label><input type="checkbox" name="colors[]" value="green" id="green"><label for="blue">Blue</label><input type="checkbox" name="colors[]" value="blue" id="blue"><input type="submit">
</form>

注意,上面程序中結構體標簽: colors[] 與復選框的名字一致,這里表示數組所以可以得到多個已選項的值。
運行程序,通過瀏覽器訪問 http://localhost:8080/ ,出現復選框表單,選擇兩個以上選項,這里選擇紅,綠兩種顏色,然后提交表單(請求發送到 http://localhost:8080/check )。
?

頁面顯示,符合提交的選項:

{"color":["red","green"]}

5. 綁定表單數據至嵌入結構體

前面已經知道通過綁定可以自動取得數據到簡單結構體對象,對有嵌入的結構體也可以通過綁定自動得到數據,不過嵌入的結構體后面不要指定標簽。

type StructA struct {FieldA string `form:"field_a"`
}type StructB struct {NestedStruct StructA // 不要指定標簽FieldB string `form:"field_b"`
}type StructC struct {NestedStructPointer *StructAFieldC string `form:"field_c"`
}type StructD struct {NestedAnonyStruct struct {FieldX string `form:"field_x"`}FieldD string `form:"field_d"`
}func GetDataB(c *gin.Context) {var b StructBc.Bind(&b)c.JSON(200, gin.H{"a": b.NestedStruct,"b": b.FieldB,})
}func GetDataC(c *gin.Context) {var b StructCc.Bind(&b)c.JSON(200, gin.H{"a": b.NestedStructPointer,"c": b.FieldC,})
}func GetDataD(c *gin.Context) {var b StructDc.Bind(&b)c.JSON(200, gin.H{"x": b.NestedAnonyStruct,"d": b.FieldD,})
}func main() {router := gin.Default()router.GET("/getb", GetDataB)router.GET("/getc", GetDataC)router.GET("/getd", GetDataD)router.Run()
}

輸入輸出結果:

curl "http://localhost:8080/getb?field_a=hello&field_b=world"
Go{"a":{"FieldA":"hello"},"b":"world"}curl "http://localhost:8080/getc?field_a=hello&field_c=world"
Go{"a":{"FieldA":"hello"},"c":"world"}curl "http://localhost:8080/getd?field_x=hello&field_d=world"
Go{"d":"world","x":{"FieldX":"hello"}}

6. 將請求體綁定到不同的結構體中

一般通過調用 ShouldBind() 方法綁定數據,但注意某些情況不能多次調用這個方法。

type formA struct {Foo string `json:"foo" xml:"foo" binding:"required"`
}type formB struct {Bar string `json:"bar" xml:"bar" binding:"required"`
}func BindHandler(c *gin.Context) {objA := formA{}objB := formB{}// c.ShouldBind 使用了 c.Request.Body ,不可重用。if errA := c.ShouldBind(&objA); errA != nil {fmt.Println(errA)c.String(http.StatusOK, `the body should be formA`)// 因為現在 c.Request.Body 是 EOF,所以這里會報錯。} else if errB := c.ShouldBind(&objB); errB != nil {fmt.Println(errB)c.String(http.StatusOK, `the body should be formB`)} else {c.String(http.StatusOK, `Success`)}
}func main() {route := gin.Default()route.Any("/bind", BindHandler)route.Run(":8080")
}

運行程序,通過瀏覽器訪問 http://localhost:8080/bind?foo=foo&bar=bar ,頁面顯示:
the body should be formA
程序運行在 Debug 模式時,在命令行運行下面命令:

curl -H "Content-Type:application/json" -v -X POST  -d '{"foo":"foo","bar":"bar"}'  http://localhost:8080/bind

命令返回:
the body should be formB
表明在第二次運行 ShouldBind() 方法時出錯,要想多次綁定,可以使用 c.ShouldBindBodyWith() 方法。

func BindHandler(c *gin.Context) {objA := formA{}objB := formB{}// ShouldBindBodyWith() 讀取 c.Request.Body 并將結果存入上下文。if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA != nil {fmt.Println(errA)c.String(http.StatusOK, `the body should be formA`)// 這時, 復用存儲在上下文中的 body 。} else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB != nil {fmt.Println(errB)c.String(http.StatusOK, `the body should be formB JSON`)// 可以接受其他格式} else {c.String(http.StatusOK, `Success`)}
}

c.ShouldBindBodyWith() 會在綁定之前將請求體存儲到上下文中。 這會對性能造成輕微影響,如果調用一次就能完成綁定的話,那就不要用這個方法。
只有某些格式需要此功能,如 JSONXMLMsgPackProtoBuf 。對于其他格式,如 QueryFormFormPostFormMultipart 可以多次調用 c.ShouldBind() 而不會造成任任何性能損失,這也是前面結構體中的標簽沒有定義 form ,只有定義 json:"foo" xml:"foo" binding:"required" 的原因。

7. 只綁定 URL Query 參數

ShouldBind() 方法支持 URL Query 參數綁定,也支持 POST 參數綁定。而 ShouldBindQuery() 方法只綁定 URL Query 參數而忽略 POST 數據。

type Person struct {Name    string `form:"name"`Address string `form:"address"`
}func startPage(c *gin.Context) {var person Personif c.ShouldBindQuery(&person) == nil {fmt.Println(person.Name)fmt.Println(person.Address)c.String(200, "Success")} else {c.String(400, "Error")}}func main() {route := gin.Default()route.Any("/bindquery", startPage)route.Run(":8080")
}

運行程序,通過瀏覽器訪問 http://localhost:8080/ ,頁面顯示 “Sucess” 。輸出結果:

[GIN-debug] GET /bindquery --> main.startPage (3 handlers)
[GIN-debug] POST /bindquery --> main.startPage (3 handlers)
[GIN-debug] PUT /bindquery --> main.startPage (3 handlers)
[GIN-debug] PATCH /bindquery --> main.startPage (3 handlers)
[GIN-debug] HEAD /bindquery --> main.startPage (3 handlers)
[GIN-debug] OPTIONS /bindquery --> main.startPage (3 handlers)
[GIN-debug] DELETE /bindquery --> main.startPage (3 handlers)
[GIN-debug] CONNECT /bindquery --> main.startPage (3 handlers)
[GIN-debug] TRACE /bindquery --> main.startPage (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
titan
cs
[GIN] 2019/07/13 - 17:06:23 | 200 | 0s | ::1 | GET /bindquery?name=titan&address=cs

輸出表明 URL Query 參數通過 GET 方法能被程序正常綁定,注意上面程序中使用了 Any() 方法,它能匹配眾多的 HTTP 方法。
如果程序繼續運行在 Debug 模式時,在命令行運行下面命令:

curl -v -X POST  -d "name=titan&address=cs"  http://localhost:8080/bindquery* Connected to localhost (::1) port 8080 (#0)
> POST /bindquery HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Length: 21
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 21 out of 21 bytes
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Date: Sat, 13 Jul 2019 17:12:37
< Content-Length: 7
<
Success

命令行的返回表明通過 POST 方法已經成功提交請求,服務端成功返回,狀態代碼: 200 ,返回內容: Success 。
控制臺輸出結果:

[GIN] 2019/07/13 - 17:12:37 | 200 | 0s | ::1 | POST /bindquery

從控制臺輸出可以看到,通過 POST 提交的數據沒有正常綁定。但是前面通過 ShouldBind() 方法可以正常綁定。這表明 ShouldBindQuery() 只綁定 URL Query 參數而忽略 POST 數據。

8. JSON 模型綁定

通過 POST 方法提交 JSON 格式數據,程序通過綁定的方式得到 JSON 數據,并傳遞給結構體,但需要指定字段標簽為 json

// 綁定 JSON
type Login struct {User     string `form:"user" json:"user" xml:"user"  binding:"required"`Password string `form:"password" json:"password" xml:"password" binding:"required"`
}func main() {router := gin.Default()// 綁定 JSON ({"user": "manu", "password": "123"})router.POST("/loginJSON", func(c *gin.Context) {var json Loginif err := c.ShouldBindJSON(&json); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if json.User != "manu" || json.Password != "123" {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})})// 綁定 HTML 表單 (user=manu&password=123)router.POST("/loginForm", func(c *gin.Context) {var form Login// 根據 Content-Type Header 推斷使用哪個綁定器。if err := c.ShouldBind(&form); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if form.User != "manu" || form.Password != "123" {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})})// 監聽并啟動服務router.Run(":8080")
}

輸出結果:

curl -v  -H 'content-type: application/json' -X POST  http://localhost:8080/loginJSON   -d '{ "user": "manu" , "password" :"123" }'> POST /loginJSON HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
> content-type: application/json
> Content-Length: 38
>
* upload completely sent off: 38 out of 38 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Sat, 13 Jul 2019 15:08:29 GMT
< Content-Length: 31
<
{"status":"you are logged in"}

9. Header 頭信息綁定

Header 也可以傳遞參數,程序通過綁定的方式得到參數值,在結構體的字段標簽上需要指定為 header

type testHeader struct {Rate   int    `header:"Rate"`Domain string `header:"Domain"`
}func main() {router := gin.Default()router.GET("/", func(c *gin.Context) {h := testHeader{}if err := c.ShouldBindHeader(&h); err != nil {c.JSON(200, err)}fmt.Printf("%#v\\n", h)c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain})})router.Run(":8080")
}

運行命令

curl -H "rate:300" -H "domain:music" http://localhost:8080/{"Domain":"music","Rate":300}

通過 curl 命令帶上自定義的頭部信息給 Handler 處理程序, ShouldBindHeader() 方法自動綁定頭部變量到結構體。
?

參考:https://gitbook.cn/gitchat/column/5dab061e7d66831b22aa0b44/topic/5dab09f37d66831b22aa0b5d

總結

以上是生活随笔為你收集整理的Gin 框架学习笔记(02)— 参数自动绑定到结构体的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。