以前阅读过 Gin 源码、并仿照Gin自己写了一个简单版的框架。

  1. Gin框架简洁版
  2. 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 方法的补充,用来对已知资源进行局部更新 。

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods

我们常用的是 GET POST,本次主要讲 GET POST,差不多可以覆盖所有的传递参数方式了。使用POSTMAN,可以方便的看到传递参数方式。

1.1 GET

共一种,这种方式称称为 Query 方式
image.png
D:\Projects\Github\NoobWu\go-samples\go-programming-tour-book\blog-service\internal\routers\router.go
image.png

1.2 POST

共四种,分别为 form-datax-www-form-urlencodedrawbinary,第五种和 GET 一样,不计算在内
image.png

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”) } ```

  • 调用

image.png

  1. 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
  2. HTTP/1.1
  3. Host: 127.0.0.1:8082
  4. Content-Type: application/x-www-form-urlencoded
  5. Cache-Control: no-cache
  6. Postman-Token: e0fdeb2e-78c2-c26d-03ad-a4a6715b3d66
  • 输出
    1. &{Query:this is query}
    2. this is param
    3. this is query
    4. this is defaultQuery
    5. this is getQuery true
    6. [this is queryArray]
    7. [this is getQueryArray] true
    8. map[1:this 2:is 3:queryMap]
    9. 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”) } ```

  • 调用

image.png

  1. CODE POST /paramtype/postformdata HTTP/1.1
  2. Host: 127.0.0.1:8082
  3. Cache-Control: no-cache
  4. Postman-Token: 83c078e3-b7de-d154-dda1-477cc4f2f450
  5. Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
  6. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  7. Content-Disposition: form-data; name="postForm"
  8. this is postForm
  9. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  10. Content-Disposition: form-data; name="defaultPostForm"
  11. this is defaultPostForm
  12. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  13. Content-Disposition: form-data; name="getPostForm"
  14. this is getPostForm
  15. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  16. Content-Disposition: form-data; name="postFormArray"
  17. this
  18. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  19. Content-Disposition: form-data; name="postFormArray"
  20. is
  21. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  22. Content-Disposition: form-data; name="postFormArray"
  23. postFormArray
  24. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  25. Content-Disposition: form-data; name="getPostFormArray"
  26. this
  27. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  28. Content-Disposition: form-data; name="getPostFormArray"
  29. is
  30. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  31. Content-Disposition: form-data; name="getPostFormArray"
  32. getPostFormArray
  33. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  34. Content-Disposition: form-data; name="postFormMap[1]"
  35. this
  36. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  37. Content-Disposition: form-data; name="postFormMap[2]"
  38. is
  39. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  40. Content-Disposition: form-data; name="postFormMap[3]"
  41. postFormMap
  42. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  43. Content-Disposition: form-data; name="getPostFormMap[1]"
  44. this
  45. ------WebKitFormBoundary7MA4YWxkTrZu0gW
  46. Content-Disposition: form-data; name="getPostFormMap[2]"
  47. is ------WebKitFormBoundary7MA4YWxkTrZu0gW
  48. Content-Disposition: form-data; name="getPostFormMap[3]"
  49. getPostFormMap
  50. ------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]

  1. <a name="uPkPA"></a>
  2. ### 2.2.2 x-www-form-urlencoded
  3. `x-www-form-urlencoded` 是`application/x-www-from-urlencoded`,将表单内的数据转换为键值对,`&`分隔。
  4. 当 **form **表单的 **action **为 **get **时,浏览器用 `x-www-form-urlencoded` 的编码方式,将表单数据编码为 (`name1=value1&name2=value2…`),然后把这个字符串 `append` 到 `url` 后面,用 `?` 分隔,跳转到这个新的 `url`。
  5. 当 `**form**`** **表单的 `action` 为 `post` 时,浏览器将 `form` 数据封装到 `http body` 中,然后发到 `**server**`。该格式不能提交文件。
  6. - 代码
  7. 同 `form-data`
  8. - 调用
  9. ![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)
  10. ```http
  11. CODE: POST /paramtype/postformdata HTTP/1.1
  12. Host: 127.0.0.1:8082
  13. Content-Type: application/x-www-form-urlencoded
  14. Cache-Control: no-cache
  15. Postman-Token: 62ffa49a-e61f-0277-db62-dd9c727e182d
  16. 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

可以上传任意格式的文本,包括textjsonxmlhtml 等。

raw 一般使用 BindShouldBind 系列函数来获取数据,两者的区别是如果输入数据无效,Bind 会将返回状态设置为 400,并且中断,ShouldBind 就没有这么狠。BindShouldBind根据Content-Type来判断是 json/xml/yaml 格式,做对应解析。

ShouldBindUriShouldBindQuery 使用起来有些技巧,代码中对此有标注。

  • 代码 ```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”) } ```

  • 调用

image.png

  1. CODE
  2. POST /paramtype/raw/I am raw name?query=I am query HTTP/1.1
  3. Host: 127.0.0.1:8082
  4. Content-Type: application/json
  5. Cache-Control: no-cache
  6. Postman-Token: 1c7f46a5-2ea2-36ac-1f9b-29dab2a90d62
  7. {"id":"1","text":"I am raw json"}
  • 输出
    1. &{Id:1 Text:I am raw json}
    2. &{Name:I am raw name}
    3. &{Query:I am query}

    2.2.4 binary

    相当于 Content-Type:application/octet-stream,从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件。

Gin 中没有找到相关的函数,文件上传使用的是 multipart/form-data,如果大家有相关资料的话,可以告知我一下。

3. 请求参数样例

参数

  1. GET:
  2. query:this is query
  3. defaultQuery:this is defaultQuery
  4. getQuery:this is getQuery
  5. queryArray:this
  6. queryArray:is
  7. queryArray:queryArray
  8. getQueryArray:this
  9. getQueryArray:is
  10. getQueryArray:getQueryArray
  11. queryMap[1]:this
  12. queryMap[2]:is
  13. queryMap[3]:queryMap
  14. getQueryMap[1]:this
  15. getQueryMap[2]:is
  16. getQueryMap[3]:getQueryMap
  17. POST:
  18. postForm:this is postForm
  19. defaultPostForm:this is defaultPostForm
  20. getPostForm:this is getPostForm
  21. postFormArray:this
  22. postFormArray:is
  23. postFormArray:postFormArray
  24. getPostFormArray:this
  25. getPostFormArray:is
  26. getPostFormArray:getPostFormArray
  27. postFormMap[1]:this
  28. postFormMap[2]:is
  29. postFormMap[3]:postFormMap
  30. getPostFormMap[1]:this
  31. getPostFormMap[2]:is
  32. getPostFormMap[3]:getPostFormMap

总结

Gin 获取输入数据还是挺复杂的,这里简单总结一下:

  1. Get 的使用Query、Param系列
  2. Post 的 form-datax-www-form-urlencoded 使用 PostForm 系列
  3. Get 和 Post 的数据都可以用Bind、ShouldBind系列,不过结构体的tag需要写正确
  4. 文件上传使用 form-data,通过函数 FormFile 或者 MultipartForm

资料

  1. https://geektutu.com/post/quick-go-gin.html Go Gin 简明教程
  2. https://github.com/gin-gonic/gin 源码
  3. https://gin-gonic.com/zh-cn/docs/ 中文文档
  4. https://www.kancloud.cn/shuangdeyu/gin_book/949436 gin中文文档
  5. https://www.kancloud.cn/adapa/gingolang/1124990 Gin框架入门到入土
  6. gin-安装,修改启动端口,get/post 请求参数,模型绑定 shouldbind,自定义验证器/表单验证
  7. 一文搞懂gin各种上传文件
  8. postman 中 form-data、x-www-form-urlencoded、raw、binary的区别


原文链接

https://zhuanlan.zhihu.com/p/377795123