以前阅读过 Gin 源码、并仿照Gin自己写了一个简单版的框架。
最近在使用的时候,发现前端调用传递参数方式各异,各种稀奇古怪的方式都会用到。这篇文章主要盘一下如何获取到参数,方便今后使用。
代码位置:https://github.com/shidawuhen/asap/tree/master/controller/paramtype
1. 类型
HTTP 请求方法有很多
序号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的页面信息,并返回实体主体。 |
2 | HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
5 | DELETE | 请求服务器删除指定的页面。 |
6 | CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
7 | OPTIONS | 允许客户端查看服务器的性能。 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
9 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
我们常用的是 GET 和 POST,本次主要讲 GET 和 POST,差不多可以覆盖所有的传递参数方式了。使用POSTMAN,可以方便的看到传递参数方式。
1.1 GET
共一种,这种方式称称为 Query 方式D:\Projects\Github\NoobWu\go-samples\go-programming-tour-book\blog-service\internal\routers\router.go
1.2 POST
共四种,分别为 form-data
、x-www-form-urlencoded
、raw
、binary
,第五种和 GET
一样,不计算在内
2. 实践
2.1 GET
2.1.1 Query
query 支持的函数很多,Param 函数需要说一下,其使用需要在router上配置
- 代码 ```go func paramTypeFunc(router *gin.Engine) { router.GET(“/paramtype/query/:param”, paramtype.Query) }
/ @date: 2021/5/24 / package paramtype
import ( “fmt” “github.com/gin-gonic/gin” “net/http” )
/**
- @Description: GET Query参数获取实例
@param c / func Query(c gin.Context) { //需定义合规结构体 rawQuery := &RawQuery{} c.ShouldBindQuery(rawQuery) fmt.Printf(“%+v \n”, rawQuery)
param := c.Param(“param”) fmt.Println(param)
query := c.Query(“query”) fmt.Println(query)
defaultQuery := c.DefaultQuery(“defaultQuery”, “no”) fmt.Println(defaultQuery)
getQuery, res := c.GetQuery(“getQuery”) fmt.Println(getQuery, res)
queryArray := c.QueryArray(“queryArray”) fmt.Println(queryArray)
getQueryArray, res := c.GetQueryArray(“getQueryArray”) fmt.Println(getQueryArray, res)
queryMap := c.QueryMap(“queryMap”) fmt.Println(queryMap)
getQueryMap, res := c.GetQueryMap(“getQueryMap”) fmt.Println(getQueryMap)
c.String(http.StatusOK, “ok”) } ```
- 调用
CODE: GET /paramtype/query/this is param?query=this is query&defaultQuery=this is defaultQuery&getQuery=this is getQuery&queryArray=this&queryArray=is&queryArray=queryArray&getQueryArray=this&getQueryArray=is&getQueryArray=getQueryArray&queryMap[1]=this&queryMap[2]=is&queryMap[3]=queryMap&getQueryMap[1]=this&getQueryMap[2]=is&getQueryMap[3]=getQueryMap
HTTP/1.1
Host: 127.0.0.1:8082
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: e0fdeb2e-78c2-c26d-03ad-a4a6715b3d66
- 输出
&{Query:this is query}
this is param
this is query
this is defaultQuery
this is getQuery true
[this is queryArray]
[this is getQueryArray] true
map[1:this 2:is 3:queryMap]
map[1:this 2:is 3:getQueryMap]
2.2 POST
2.2.1 form-data
form-data
的获取正规正矩。form-data
就是 http 请求中的 multipart/form-data
,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有Content-Type
来说明文件类型;content-disposition
,用来说明字段的一些信息;
如果是文件,可以通过 FormFile
或者 MultipartForm
获取文件内容,FormFile
获取一个,MultipartForm
获取多个。使用 SaveUploadedFile
存储文件。
- 代码 ```go / @date: 2021/5/25 / package paramtype
import ( “fmt” “github.com/gin-gonic/gin” “net/http” )
type PostFormStruct struct {
GetPostForm string json:"getPostForm" uri:"getPostForm" form:"getPostForm"
}
/**
- @Author: POST form数据获取
- @Description:
@param c / func PostFormData(c gin.Context) { //需定义合规结构体 postFormStruct := &PostFormStruct{} c.ShouldBind(postFormStruct) fmt.Printf(“%+v \n”, postFormStruct)
postForm := c.PostForm(“postForm”) fmt.Println(postForm)
defaultPostForm := c.DefaultPostForm(“defaultPostForm”, “no”) fmt.Println(defaultPostForm)
getPostForm, res := c.GetPostForm(“getPostForm”) fmt.Println(getPostForm, res)
postFormArray := c.PostFormArray(“postFormArray”) fmt.Println(postFormArray)
getPostFormArray, res := c.GetPostFormArray(“getPostFormArray”) fmt.Println(getPostFormArray, res)
postFormMap := c.PostFormMap(“postFormMap”) fmt.Println(postFormMap)
getPostFormMap, res := c.GetPostFormMap(“getPostFormMap”) fmt.Println(getPostFormMap)
c.String(http.StatusOK, “ok”) } ```
- 调用
CODE: POST /paramtype/postformdata HTTP/1.1
Host: 127.0.0.1:8082
Cache-Control: no-cache
Postman-Token: 83c078e3-b7de-d154-dda1-477cc4f2f450
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="postForm"
this is postForm
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="defaultPostForm"
this is defaultPostForm
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="getPostForm"
this is getPostForm
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="postFormArray"
this
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="postFormArray"
is
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="postFormArray"
postFormArray
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="getPostFormArray"
this
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="getPostFormArray"
is
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="getPostFormArray"
getPostFormArray
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="postFormMap[1]"
this
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="postFormMap[2]"
is
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="postFormMap[3]"
postFormMap
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="getPostFormMap[1]"
this
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="getPostFormMap[2]"
is ------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="getPostFormMap[3]"
getPostFormMap
------WebKitFormBoundary7MA4YWxkTrZu0gW--
- 输出 ```http &{GetPostForm:this is getPostForm}
this is postForm
this is defaultPostForm
this is getPostForm true
[this is postFormArray]
[this is getPostFormArray] true
map[1:this 2:is 3:postFormMap]
map[1:this 2:is 3:getPostFormMap]
<a name="uPkPA"></a>
### 2.2.2 x-www-form-urlencoded
`x-www-form-urlencoded` 是`application/x-www-from-urlencoded`,将表单内的数据转换为键值对,`&`分隔。
当 **form **表单的 **action **为 **get **时,浏览器用 `x-www-form-urlencoded` 的编码方式,将表单数据编码为 (`name1=value1&name2=value2…`),然后把这个字符串 `append` 到 `url` 后面,用 `?` 分隔,跳转到这个新的 `url`。
当 `**form**`** **表单的 `action` 为 `post` 时,浏览器将 `form` 数据封装到 `http body` 中,然后发到 `**server**`。该格式不能提交文件。
- 代码
同 `form-data`
- 调用
![image.png](https://cdn.nlark.com/yuque/0/2021/png/161237/1633850484430-de699652-0b03-41fb-9d0d-5b0ff0a139b0.png#clientId=uca79f1e4-46e3-4&from=paste&id=u27a89e93&margin=%5Bobject%20Object%5D&name=image.png&originHeight=671&originWidth=1080&originalType=url&ratio=1&size=190339&status=done&style=none&taskId=uef7d5dfe-b1cb-4f58-8581-ff189346b48)
```http
CODE: POST /paramtype/postformdata HTTP/1.1
Host: 127.0.0.1:8082
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: 62ffa49a-e61f-0277-db62-dd9c727e182d
postForm=this+is+postForm&defaultPostForm=this+is+defaultPostForm&getPostForm=this+is+getPostForm&postFormArray=this&postFormArray=is&postFormArray=postFormArray&getPostFormArray=this&getPostFormArray=is&getPostFormArray=getPostFormArray&postFormMap%5B1%5D=this&postFormMap%5B2%5D=is&postFormMap%5B3%5D=postFormMap&getPostFormMap%5B1%5D=this&getPostFormMap%5B2%5D=is&getPostFormMap%5B3%5D=getPostFormMap
- 输出
同 form-data
2.2.3 raw
可以上传任意格式的文本,包括text
、json
、xml
、html
等。
raw
一般使用 Bind
和 ShouldBind
系列函数来获取数据,两者的区别是如果输入数据无效,Bind
会将返回状态设置为 400
,并且中断,ShouldBind
就没有这么狠。Bind
和ShouldBind
根据Content-Type
来判断是 json/xml/yaml
格式,做对应解析。
ShouldBindUri
和 ShouldBindQuery
使用起来有些技巧,代码中对此有标注。
- 代码 ```go router.POST(“/paramtype/raw/:name”, paramtype.Raw)
/ @date: 2021/5/26 / package paramtype
import ( “fmt” “github.com/gin-gonic/gin” “net/http” )
type RawStruct struct { Id string Text string }
type RawUri struct {
Name string json:"name" uri:"name"
}
type RawQuery struct {
Query string json:"query" uri:"query" form:"query"
}
/**
- @Description: POST Raw数据获取
@param c / func Raw(c gin.Context) {
raw := &RawStruct{} c.ShouldBind(raw) fmt.Printf(“%+v \n”, raw)
//需定义合规结构体 rawUri := &RawUri{} c.ShouldBindUri(rawUri) fmt.Printf(“%+v \n”, rawUri)
//需定义合规结构体 rawQuery := &RawQuery{} c.ShouldBindQuery(rawQuery) fmt.Printf(“%+v \n”, rawQuery)
c.String(http.StatusOK, “ok”) } ```
- 调用
CODE:
POST /paramtype/raw/I am raw name?query=I am query HTTP/1.1
Host: 127.0.0.1:8082
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 1c7f46a5-2ea2-36ac-1f9b-29dab2a90d62
{"id":"1","text":"I am raw json"}
- 输出
&{Id:1 Text:I am raw json}
&{Name:I am raw name}
&{Query:I am query}
2.2.4 binary
相当于Content-Type:application/octet-stream
,从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件。
Gin
中没有找到相关的函数,文件上传使用的是 multipart/form-data
,如果大家有相关资料的话,可以告知我一下。
3. 请求参数样例
参数
GET:
query:this is query
defaultQuery:this is defaultQuery
getQuery:this is getQuery
queryArray:this
queryArray:is
queryArray:queryArray
getQueryArray:this
getQueryArray:is
getQueryArray:getQueryArray
queryMap[1]:this
queryMap[2]:is
queryMap[3]:queryMap
getQueryMap[1]:this
getQueryMap[2]:is
getQueryMap[3]:getQueryMap
POST:
postForm:this is postForm
defaultPostForm:this is defaultPostForm
getPostForm:this is getPostForm
postFormArray:this
postFormArray:is
postFormArray:postFormArray
getPostFormArray:this
getPostFormArray:is
getPostFormArray:getPostFormArray
postFormMap[1]:this
postFormMap[2]:is
postFormMap[3]:postFormMap
getPostFormMap[1]:this
getPostFormMap[2]:is
getPostFormMap[3]:getPostFormMap
总结
Gin 获取输入数据还是挺复杂的,这里简单总结一下:
- Get 的使用Query、Param系列
- Post 的
form-data
和x-www-form-urlencoded
使用 PostForm 系列 - Get 和 Post 的数据都可以用Bind、ShouldBind系列,不过结构体的tag需要写正确
- 文件上传使用
form-data
,通过函数 FormFile 或者 MultipartForm
资料
- https://geektutu.com/post/quick-go-gin.html Go Gin 简明教程
- https://github.com/gin-gonic/gin 源码
- https://gin-gonic.com/zh-cn/docs/ 中文文档
- https://www.kancloud.cn/shuangdeyu/gin_book/949436 gin中文文档
- https://www.kancloud.cn/adapa/gingolang/1124990 Gin框架入门到入土
- gin-安装,修改启动端口,
get/post
请求参数,模型绑定shouldbind
,自定义验证器/表单验证 - 一文搞懂gin各种上传文件
- postman 中 form-data、x-www-form-urlencoded、raw、binary的区别