以前阅读过 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]=getQueryMapHTTP/1.1Host: 127.0.0.1:8082Content-Type: application/x-www-form-urlencodedCache-Control: no-cachePostman-Token: e0fdeb2e-78c2-c26d-03ad-a4a6715b3d66
- 输出
&{Query:this is query}this is paramthis is querythis is defaultQuerythis is getQuery true[this is queryArray][this is getQueryArray] truemap[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.1Host: 127.0.0.1:8082Cache-Control: no-cachePostman-Token: 83c078e3-b7de-d154-dda1-477cc4f2f450Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="postForm"this is postForm------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="defaultPostForm"this is defaultPostForm------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="getPostForm"this is getPostForm------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="postFormArray"this------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="postFormArray"is------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="postFormArray"postFormArray------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="getPostFormArray"this------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="getPostFormArray"is------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="getPostFormArray"getPostFormArray------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="postFormMap[1]"this------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="postFormMap[2]"is------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="postFormMap[3]"postFormMap------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="getPostFormMap[1]"this------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="getPostFormMap[2]"is ------WebKitFormBoundary7MA4YWxkTrZu0gWContent-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`- 调用```httpCODE: POST /paramtype/postformdata HTTP/1.1Host: 127.0.0.1:8082Content-Type: application/x-www-form-urlencodedCache-Control: no-cachePostman-Token: 62ffa49a-e61f-0277-db62-dd9c727e182dpostForm=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.1Host: 127.0.0.1:8082Content-Type: application/jsonCache-Control: no-cachePostman-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 querydefaultQuery:this is defaultQuerygetQuery:this is getQueryqueryArray:thisqueryArray:isqueryArray:queryArraygetQueryArray:thisgetQueryArray:isgetQueryArray:getQueryArrayqueryMap[1]:thisqueryMap[2]:isqueryMap[3]:queryMapgetQueryMap[1]:thisgetQueryMap[2]:isgetQueryMap[3]:getQueryMapPOST:postForm:this is postFormdefaultPostForm:this is defaultPostFormgetPostForm:this is getPostFormpostFormArray:thispostFormArray:ispostFormArray:postFormArraygetPostFormArray:thisgetPostFormArray:isgetPostFormArray:getPostFormArraypostFormMap[1]:thispostFormMap[2]:ispostFormMap[3]:postFormMapgetPostFormMap[1]:thisgetPostFormMap[2]:isgetPostFormMap[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的区别
 
