一般客户端向服务器发送请求时,需要指定请求的方式、请求的包含内容(数据类型和请求体数据)、目标URL等信息和版本协议等。 今天主要说明请求包含的内容,也就是数据类型和请求数据以及在Gin框架中的对请求数据的参数绑定。
- func (c *Context) ShouldBind(obj any) error{}
- func (c *Context) ShouldBindJSON(obj any) error{}
- func (c *Context) ShouldBindQuery(obj any) error{}
Content-Type
首先了解一下什么是
Content-Type,它是HTTP协议中的一个头部字段,用于指示HTTP消息中实体主体的数据类型。它告诉接收方如何解析和处理请求或响应的主体数据。 举几个常见的Content-Type
application/json:JSON数据类型,用于传输JSON格式的数据。例如,用于表示API请求和响应中的JSON数据。application/xml:XML数据类型,用于传输XML格式的数据。例如,用于表示Web服务之间的数据交换。application/x-www-form-urlencoded:表单数据类型,常用于HTML表单提交数据。例如,通过POST请求将表单数据发送给服务器。multipart/form-data:多部分表单数据类型,用于上传文件或二进制数据。例如,通过HTML表单上传文件到服务器。text/plain:纯文本数据类型,用于传输普通文本数据。image/jpeg:JPEG图像类型,用于传输JPEG格式的图片。audio/mpeg:MPEG音频类型,用于传输MPEG格式的音频文件。video/mp4:MP4视频类型,用于传输MP4格式的视频文件。
在HTTP请求中,通常由客户端在发送请求时设置Content-Type头部字段,以告知服务器发送的数据类型。在HTTP响应中,服务器会设置Content-Type头部字段,指示客户端接收到的数据类型。
JSON和form-data
接下来对请求体中最常用的两种数据格式做一个简单说明。
- JSON
- 结构:JSON是一种轻量级的数据交换格式,基于键值对的方式组织数据。数据以键值对的形式表示,并使用大括号{}将整个数据包围。键(Key)是字符串,值(Value)可以是字符串、数字、布尔值、数组或其他JSON对象。
- 编码:在JSON中,键和字符串的值都需要用双引号括起来,例如:"key": "value"。数字和布尔值没有引号,例如:"age": 30。
- 示例:
{ "name": "whyand666", "age": 18, "email": "1780006511@qq.com", } - Form
- 表单数据是通过键值对的形式传递,每个字段都有一个唯一的名称(键),以及一个对应的值。表单数据通常用于提交HTML表单,以便服务器处理用户输入。
- 编码:表单数据使用URL编码格式,键值对之间用等号(=)连接,多个字段用&符号连接。特殊字符会进行URL编码,例如空格会被编码为+或%20。
- 示例:
name=why+and666&age=18&email=john.1780006511%40qq.com
总结:Form表单形式和JSON形式的入参在数据传输方式和数据结构上有所不同。Form表单适用于传递用户输入信息和文件上传,而JSON适用于数据交换和存储,具有更灵活和结构化的特点。至于具体的优点缺点自己查。
准备和环境
首先准备一个接口,这里只展示接口代码的一部分。
func (u *HandlerUser) register(c *gin.Context) {
......
var req user.RegisterReq
if err := c.ShouldBind(&req); err != nil {
zap.S().Errorf("c.ShouldBind(req) failed ,err:%v", err)
c.JSON(http.StatusOK, result.Fail(http.StatusBadRequest, "参数格式有误"))
return
}
fmt.Println("req:", req)
......
}
参数定义
type RegisterReq struct {
Email string `json:"email" form:"email"`
Name string `json:"name" form:"name"`
Password string `json:"password" form:"password"`
Password2 string `json:"password2" form:"password2"`
Mobile string `json:"mobile" form:"mobile"`
Captcha string `json:"captcha" form:"captcha"`
}
环境为go1.19,win10,apifox上打请求,Gin框架
1. ShouldBind()
// ShouldBind checks the Method and Content-Type to select a binding engine automatically,
// Depending on the "Content-Type" header different bindings are used, for example:
//
// "application/json" --> JSON binding
// "application/xml" --> XML binding
//
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
// It decodes the json payload into the struct specified as a pointer.
// Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid.
func (c *Context) ShouldBind(obj any) error {
b := binding.Default(c.Request.Method, c.ContentType())
return c.ShouldBindWith(obj, b)
}
context.ShouldBind()是一个通用的方法,用于从 HTTP 请求中解析数据,并将其绑定到目标结构体中。这个方法会根据请求的 Content-Type 自动选择合适的解析方式,目前支持 JSON、XML、Form 表单等格式。 参数obj是一个指向目标结构体的指针,用于将解析后的数据填充到这个结构体中。
使用
context.ShouldBind()的好处是不需要在代码中显式指定数据的来源(JSON、XML、Form 等),而是让 Gin 根据请求的 Content-Type 自动识别和解析数据。若无法正确解析数据或填充到目标结构体中,会返回相应的错误。
测试1. 通过 form-data 提交数据(json相同)
- 提交成功:至于
boundary后面的一堆数字是是用于分隔不同部分的边界字符串。
测试2. 通过 GET-Query 请求
- 请求成功:GET请求通常用于向服务器请求数据,而不是向服务器提交数据。因此,GET请求不包含请求体所以在Content-Type头部字段中没有值。GET请求中的参数通常会以查询参数的形式出现在URL中。
测试3. 错误,通过 form-data 提交数据
注意这里改变了req的定义
- 失败:直接报panic,因为在使用
c.ShouldBind()时传入了一个未初始化的指针变量!!! - 一定要注意这个问题!!! 这里可以这么定义
req := new(user.RegisterReq)
2. ShouldBindJSON()
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
func (c *Context) ShouldBindJSON(obj any) error {
return c.ShouldBindWith(obj, binding.JSON)
}
测试1. 通过 json 提交数据
- 成功:
测试2. 通过 form 提交数据
- 失败:这里的200只是请求成功。
- 看这个报错,结合一下form的编码格式,说明ShouldBindJSON解析不了form。
3. ShouldBindQuery()
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
func (c *Context) ShouldBindQuery(obj any) error {
return c.ShouldBindWith(obj, binding.Query)
}
在HTTP请求中,GET请求通常将查询参数(Query参数)附加在URL的末尾,而不是放在请求体中。查询参数是以
key=value的形式出现在URL的问号(?)后面,并使用&符号分隔多个参数。
测试1. 通过 GET-Query 打请求
- 成功:有数据,GET请求不包含请求体,所以在Content-Type头部字段中没有值。
测试2. 通过 JSON 提交数据
- 失败:发现
Content-Type为 json,绑定的结构体中并没有数据。
总结
context.ShouldBind()是 Gin 框架中用于将 HTTP 请求数据绑定到目标结构体的通用方法。通过使用该方法,可以简化数据解析和绑定的过程。它会根据请求的 Content-Type 自动选择解析方式,然后将数据填充到结构体中。context.ShouldBindJSON()用于将 HTTP 请求的 JSON 数据绑定到目标结构体的方法。ShouldBindQuery()用于将HTTP请求中的查询参数绑定到目标结构体的方法。
它们的执行效率和数据的类型和大小以及请求的内容有关。 一般我自己用的话,GET请求就用ShouldBindQuery。对于json数据,我会写明是ShouldBindJSON(),form数据就用ShouldBind()了。
结尾:
这是我第一次在掘金上发文章,也是发现自己对这些知识点没有一个归纳,刚好也是字节青训营么,嘻嘻。 各位读者如果对文章有什么疑问或者补充,可以直接在评论区表达您的疑问,私信我也可以。 希望大家能够通过本文章学习到这些知识,谢谢大家!