共计 4029 个字符,预计需要花费 11 分钟才能阅读完成。
大家好,我是渔夫子。
在 gin 框架中,咱们晓得用 bind 函数(或 bindXXX 函数)可能将申请体中的参数绑定到对应的构造体上。同时,你也会发现在 gin 中有很多 bind 或 bindXXX 函数,比方 ShouldBind、ShouldBindQuery、ShouldBindHeader、ShouldBindJSON 等等。那么,他们之间有什么不同呢?本文带你深刻理解这些 bind 函数的应用。
一、bind 的根本作用
在 gin 框架或其余所有 web 框架中,bind 或 bindXXX 函数(后文中咱们对立都叫 bind 函数)的作用就是将 申请体中的参数值绑定到对应的构造体上,以不便后续业务逻辑的解决。
接下来咱们看一个简略的应用例子,该实例是冀望客户端发送一个 JSON 格局的申请体,而后通过 JSON 标签绑定到 LoginRequest 构造体上。如下:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
func main() {g := gin.New()
g.POST("/login", func(ctx *gin.Context) {r := &LoginRequest{}
ctx.ShouldBind(r)
fmt.Printf("login-request:%+v\n", r)
})
g.Run(":9090")
}
运行上述示例代码,并在 postman 中或应用 curl 给 http://localhost:9090/login 发送申请,申请体是:
curl -X POST -H "Content-Type:application/json" http://localhost:9090/login -d '{"username":"yufuzi","password":"123456}'
在代码中,咱们通过 ctx.ShouldBind(r)函数,将申请体的内容绑定到了 LoginRequest 类型的 r 变量上。
咱们通过 ShouldBind 函数的源代码能够梳理到绑定函数的个别流程:
1、调用 ctx.ShouldBind 函数
2、ShouldBind 函数依据申请的办法(POST 还是 GET)以及 Content-Type 获取具体的 bind 实例。如是 POST 申请且申请体是 JSON 格局,那么就返回 jsonBinding 构造体实例。
3、调用 ctx.ShouldBindWith 函数
4、ShouldBindWith 函数调用具体的绑定实例的 Bind 办法。例如 jsonBinding.Bind 函数
5、将 request 中的 Body(或 Form、Header、Query)中的申请值绑定到对应的构造体上。
其大抵流程如下:
二、申请数据起源
由第一节咱们理解到,数据来源于客户端发来的申请。那么,在一次 http 申请中,都能够通过哪里来携带参数呢?依据 http 协定的规范,能够通过 url 中的查问参数,申请头、申请体等路径将参数传递给服务端。
在申请体中参数能够是不同的格局,比方 JSON 格局、XML 格局、YAML 格局、TOML 格局、Protobuf message 等。也能够是 form 表单的模式。
有了起源,接下来看看各个 bind 函数是如何把不同数据源的数据绑定到构造体上的。
三、bind 及其 bindXXX 函数
为了可能不便解析不同起源的申请数据及不同格局的数据,在 gin 框架中就对应了不同的 bind 及 bindXXX 函数来解析对应的申请数据。以下就是对应的数据起源及不同格局的函数。
ShouldBindQuery 函数
首先是来源于 url 地址中的查问参数,对应的解析函数是 ShouldBindQuery
,构造体中通过给字段减少query
标签即可关联。如下:
ShouldBindHeader 函数
其次是来源于申请头中的参数,对应的解析函数是 ShouldBindHeader,构造体中通过给字段减少 header
标签即可关联。如下:
ShouldBindXXX 函数
而后是来源于申请体中的参数,这个稍微简单。若申请体是一般的文本格式的话,能够是 JSON、XML、TOML、YAML 或者 protobuf、msgpack 格局。能够对应 ShouldBindXXX 函数,如下:
若申请体是以表单模式发送数据的,会有 formBinding、formPostBinding 以及 formMultipartBinding 三个构造体。那这三个 binding 有什么区别呢?要想搞清楚三个构造体之间的区别,就要从 form 的 enctype 属性说起。
form 的 enctype 属性
在 html 中,咱们发送表单时个别会用 <form> 标签,但 form 标签有一个 enctype
属性,该属性个别有两个值:multipart/form-data和application/x-www-form-urlencoded。这两个值什么意思呢?
属性为 application/x-www-form-urlencoded
enctype 为该属性时,代表将 form 中的值在发送给服务端时,会将 form 中的值组织成 key1=value1&key2=value2 这样的类型发送。如下:
<form action="http://localhost:9090/login?utm_source=login" method="POST" enctype="application/x-www-form-urlencoded">
<input type="text" name="username" value="yufuzi" />
<input type="text" name="password" />
<input type="file" name="f" />
<input type="submit" value="submit" />
</form>
当咱们提交订单时,浏览器发送给服务端的申请参数会被编码成如下模式:
属性值为 multipart/form-data
该属性值代表表白是能够发送二进制的数据,比方文件。如下:
<form action="http://localhost:9090/login?utm_source=login" method="POST" enctype="multipart/form-data">
<input type="text" name="username" value="yufuzi" />
<input type="text" name="password" />
<input type="file" name="f" />
<input type="submit" value="submit" />
</form>
同时,咱们还发现在 post 的表单中,action 的地址还能够带查问参数,即?utm_source=login 参数。所以一个表单中可能携带参数的中央有:
- url 地址中的查问参数。
- 表单的值域。即 input 控件。
依据发送时的编码方式又能够将值域参数分为按 url 查问参数编码的形式和混合形式。
gin 申请中的 Form、PostForm、MultipartForm 构造体
依据申请参数起源的不同,在 gin 中也有对应的 Form 对象来承载对应的值。在 go 的 net/http 包的 Request 构造体中,咱们发现有 Form、PostForm、MultipartForm 对象。这些对象就是别离承载不同起源的申请参数的。
- Form 对象 :其值来源于url 地址中的查问参数 和表单中的值域 两局部。以上述 login 的表单为例,Form 中的值则是 utm_source=login, username=yufuzi,password=123456
- PostForm 对象:其值来源于表单中的值域。以上述 login 的表单为例,PostForm 中的值则是 username=yufuzi,password=123456
- MultipartForm 对象:其值来源于表单中的文件的值。以上述 login 的表单为例,MultipartForm 中的值分为两局部,一部分是 Values 值,保留的是 username=yufuzi,password=123456 的值。一部分是文件的值,保留的是 f 中的文件句柄。
当然,在绑定申请参数的时候也有对应的 bind 办法。
在 gin 中对应的办法为ctx.ShouldBindWith(obj, binding.Form)
。当然,在应用 ctx.ShouldBind 办法时,默认也是绑定 request.Form 中的数据到构造体。
通过 ctx.ShouldBindWith(obj, binding.FormPost)函数,能够将 request.PostForm 中的申请参数值绑定到对应的构造体上,如下:
通过 ctx.ShouldBindWith(obj, binding.MIMEMultipartPOSTForm)函数,能够将 request.PostForm 中的申请参数值绑定到对应的构造体上,如下:
gin 中 bind 函数的残缺层级构造
在 gin 中,要将申请体绑定到构造体的操作的入口是从 context 包的函数开始的,而后是通过 ShoudBindWith 函数对接 binding 包中的具体的解析对象。最初,通过不同的函数将申请中不同的参数解析到构造体上。如下图所示:
四、总结
本文解说了在 gin 框架中申请体的内容是如何绑定到对应构造体上的。同时剖析了在 gin 中不同的 bind 函数以及 bindXXX 函数之间的差别。在其余框架中其实也相似,因为在底层的 http 包中是按标准协议传递参数的,下层只是实现不同而已。
特地举荐:一个专一 go 我的项目实战、我的项目中踩坑教训及避坑指南、各种好玩的 go 工具的公众号:「Go 学堂」,专一实用性,十分值得大家关注。关注送《100 个 go 常见的谬误》pdf 文档。