Gin 框架优雅的获取所有的请求参数


Gin 框架优雅的获取所有的请求参数

嗯,话不多说,直接看代码 ^_^

将以下代码写入某个 .go 文件中,比如,我这里写入 gin_demo.go 文件中,然后执行 go run gin_demo.go

主要代码


package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.Any("/foo", func(c *gin.Context) {
		inputs, err := RequestInputs(c)
		if err != nil {
			c.JSON(500, gin.H{
				"msg": err.Error(),
			})
		} else {
			data := gin.H{
				"method": c.Request.Method,
				"params": inputs,
			}
			fmt.Printf("返回值为 ====> %#v \n", data)
			c.JSON(200, data)
		}
	})

	r.Run(":9501")
}

// RequestInputs 获取所有参数
func RequestInputs(c *gin.Context) (map[string]interface{}, error) {

	const defaultMemory = 32 << 20
	contentType := c.ContentType()

	var (
		dataMap  = make(map[string]interface{})
		queryMap = make(map[string]interface{})
		postMap  = make(map[string]interface{})
	)

	// @see gin@v1.7.7/binding/query.go ==> func (queryBinding) Bind(req *http.Request, obj interface{})
	for k := range c.Request.URL.Query() {
		queryMap[k] = c.Query(k)
	}

	if "application/json" == contentType {
		var bodyBytes []byte
		if c.Request.Body != nil {
			bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
		}
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
		// @see gin@v1.7.7/binding/json.go ==> func (jsonBinding) Bind(req *http.Request, obj interface{})
		if c.Request != nil && c.Request.Body != nil {
			if err := json.NewDecoder(c.Request.Body).Decode(&postMap); err != nil {
				return nil, err
			}
		}
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
	} else if "multipart/form-data" == contentType {
		// @see gin@v1.7.7/binding/form.go ==> func (formMultipartBinding) Bind(req *http.Request, obj interface{})
		if err := c.Request.ParseMultipartForm(defaultMemory); err != nil {
			return nil, err
		}
		for k, v := range c.Request.PostForm {
			if len(v) > 1 {
				postMap[k] = v
			} else if len(v) == 1 {
				postMap[k] = v[0]
			}
		}
	} else {
		// ParseForm 解析 URL 中的查询字符串,并将解析结果更新到 r.Form 字段
		// 对于 POST 或 PUT 请求,ParseForm 还会将 body 当作表单解析,
		// 并将结果既更新到 r.PostForm 也更新到 r.Form。解析结果中,
		// POST 或 PUT 请求主体要优先于 URL 查询字符串(同名变量,主体的值在查询字符串的值前面)
		// @see gin@v1.7.7/binding/form.go ==> func (formBinding) Bind(req *http.Request, obj interface{})
		if err := c.Request.ParseForm(); err != nil {
			return nil, err
		}
		if err := c.Request.ParseMultipartForm(defaultMemory); err != nil {
			if err != http.ErrNotMultipart {
				return nil, err
			}
		}
		for k, v := range c.Request.PostForm {
			if len(v) > 1 {
				postMap[k] = v
			} else if len(v) == 1 {
				postMap[k] = v[0]
			}
		}
	}

	var mu sync.RWMutex
	for k, v := range queryMap {
		mu.Lock()
		dataMap[k] = v
		mu.Unlock()
	}
	for k, v := range postMap {
		mu.Lock()
		dataMap[k] = v
		mu.Unlock()
	}

	return dataMap, nil
}

测试

  • get 请求,只有 query 参数时
curl --location --request GET '127.0.0.1:9501/foo?field1=value1&field2=false&field3=12&field4=4.78'

# 返回值为 ====> gin.H{"method":"GET", "params":map[string]interface {}{"field1":"value1", "field2":"false", "field3":"12", "field4":"4.78"}}
  • get 请求,有 query 参数,且有 multipart/form-data 参数时
curl --location --request GET '127.0.0.1:9501/foo?field1=value1&field2=false&field3=12&field4=4.78' \
--form 'f1="vv1"' \
--form 'f2="vv2"' \
--form 'f3="false"' \
--form 'f4="7.08"' \
--form 'f5="张三"' \
--form 'f6="88"' \
--form 'f7[]="hello"' \
--form 'f7[]="world"' \
--form 'field1="覆盖了 field1"'

# 返回值为 ====> gin.H{"method":"GET", "params":map[string]interface {}{"f1":"vv1", "f2":"vv2", "f3":"false", "f4":"7.08", "f5":"张三", "f6":"88", "f7[]":[]string{"hello", "world"}, "field1":"覆盖了 field1", "field2":"false", "field3":"12", "field4":"4.78"}}

#{
#   "method": "GET",
#   "params": {
#       "f1": "vv1",
#       "f2": "vv2",
#       "f3": "false",
#       "f4": "7.08",
#       "f5": "张三",
#       "f6": "88",
#       "f7[]": [
#           "hello",
#           "world"
#       ],
#       "field1": "覆盖了 field1",
#       "field2": "false",
#       "field3": "12",
#       "field4": "4.78"
#   }
#}
  • post 请求,既有 query 参数,也有 application/x-www-form-urlencoded 参数时
curl --location --request POST '127.0.0.1:9501/foo?field1=value1&field2=false&field3=12&field4=4.78' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'f1=vv1' \
--data-urlencode 'f2=vv2' \
--data-urlencode 'f3=false' \
--data-urlencode 'f4=7.08' \
--data-urlencode 'f5=张三' \
--data-urlencode 'f6=88' \
--data-urlencode 'f7[]=hello' \
--data-urlencode 'f7[]=world' \
--data-urlencode 'field1=覆盖了 field1'

# 返回值为 ====> gin.H{"method":"POST", "params":map[string]interface {}{"f1":"vv1", "f2":"vv2", "f3":"false", "f4":"7.08", "f5":"张三", "f6":"88", "f7[]":[]string{"hello", "world"}, "field1":"覆盖了 field1", "field2":"false", "field3":"12", "field4":"4.78"}}

#{
#   "method": "POST",
#   "params": {
#       "f1": "vv1",
#       "f2": "vv2",
#       "f3": "false",
#       "f4": "7.08",
#       "f5": "张三",
#       "f6": "88",
#       "f7[]": [
#           "hello",
#           "world"
#       ],
#       "field1": "覆盖了 field1",
#       "field2": "false",
#       "field3": "12",
#       "field4": "4.78"
#   }
#}
  • post 请求,既有 query 参数,也有 application/json 参数时

curl --location --request POST '127.0.0.1:9501/foo?field1=value1&field2=false&field3=12&field4=4.78' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "张t",
    "account": "",
    "introduction": null,
    "avatar": "/avatar/2022/05/11/ruQvUHOF-6512bd43d9caa6e02c990b0a82652dca-20220511111053.jpg",
    "favorite": [
        "football",
        "basketball",
        "pingpong-ball"
    ],
    "num": [
        1,
        5,
        8
    ],
    "books": {
        "name": "bookA",
        "price": 18.98
    },
    "students": [
        {
            "name": "张三",
            "age": 18,
            "height": 180.00
        },
        {
            "name": "李四",
            "age": 24.5,
            "height": 165.55
        }
    ],
    "field1": "覆盖了 field1"
}'

# 返回值为 ====> gin.H{"method":"POST", "params":map[string]interface {}{"account":"", "avatar":"/avatar/2022/05/11/ruQvUHOF-6512bd43d9caa6e02c990b0a82652dca-20220511111053.jpg", "books":map[string]interface {}{"name":"bookA", "price":18.98}, "favorite":[]interface {ootball", "basketball", "pingpong-ball"}, "field1":"覆盖了 field1", "field2":"false", "field3":"12", "field4":"4.78", "introduction":interface {}(nil), "name":"张t", "num":[]interface {}{1, 5, 8}, "students":[]interface {}{map[string]interface {}{"age":18, "height", "name":"张三"}, map[string]interface {}{"age":24.5, "height":165.55, "name":"李四"}}}}

#{
#    "method": "POST",
#    "params": {
#        "account": "",
#        "avatar": "/avatar/2022/05/11/ruQvUHOF-6512bd43d9caa6e02c990b0a82652dca-20220511111053.jpg",
#        "books": {
#            "name": "bookA",
#            "price": 18.98
#        },
#        "favorite": [
#            "football",
#            "basketball",
#            "pingpong-ball"
#        ],
#        "field1": "覆盖了 field1",
#        "field2": "false",
#        "field3": "12",
#        "field4": "4.78",
#        "introduction": null,
#        "name": "张t",
#        "num": [
#            1,
#            5,
#            8
#        ],
#        "students": [
#            {
#                "age": 18,
#                "height": 180,
#                "name": "张三"
#            },
#            {
#                "age": 24.5,
#                "height": 165.55,
#                "name": "李四"
#            }
#        ]
#    }
#}

以上代码通过阅读 gin 框架 ShouldBind 方法绑定参数逻辑编写。经过粗略测试,均可以获取所有的参数,并将结果绑定到 map[string]interface{} 中。


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
评论
  目录