gin03:请求中的参数

上次我们探讨了gin中的路径参数是如何获取,这次我们将目光转向请求中的参数

示例:解析请求中的参数

css 复制代码
func main() {
    router := gin.Default()
    router.GET("/welcome", func(c *gin.Context) {
       firstname := c.DefaultQuery("firstname", "Guest")
       lastname := c.Query("lastname")
       c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
    })
    router.Run(":9090")
}

类似的,我们想知道,在DefaultQuery以及Query中到底发生了什么。

获取参数的底层讨论

go 复制代码
func (c *Context) DefaultQuery(key, defaultValue string) string {
    if value, ok := c.GetQuery(key); ok {
       return value
    }
    return defaultValue
}

在DefaultQuery中,实际是调用了Query,如果存在对应的key,就直接返回value,否则返回默认值。

go 复制代码
func (c *Context) GetQuery(key string) (string, bool) {
    if values, ok := c.GetQueryArray(key); ok {
       return values[0], ok
    }
    return "", false
}

在Query中,调用了GetQueryArray方法,如果对应的key,就返回values[0],否则返回空字符串

go 复制代码
func (c *Context) GetQueryArray(key string) (values []string, ok bool) {
    c.initQueryCache()
    values, ok = c.queryCache[key]
    return
}

在GetQueryArray中,先调用initQueryCache,再从queryCache中获取values

queryCache是Context中的字段,它的类型是url.Values,其本质是

go 复制代码
type Values map[string][]string

先查看initQueryCache

go 复制代码
func (c *Context) initQueryCache() {
    if c.queryCache == nil {
       if c.Request != nil && c.Request.URL != nil {
          c.queryCache = c.Request.URL.Query()
       } else {
          c.queryCache = url.Values{}
       }
    }
}

如果queryCache是空指针,将会获取Request.URL.Query()的值

go 复制代码
func (u *URL) Query() Values {
    v, _ := ParseQuery(u.RawQuery)
    return v
}

在本例中,u.RawQuery的值是firstname=si&lastname=li,将会利用ParseQuery解析这个字符串

go 复制代码
func ParseQuery(query string) (Values, error) {
    m := make(Values)
    err := parseQuery(m, query)
    return m, err
}

继续使用parseQuery解析字符串

go 复制代码
func parseQuery(m Values, query string) (err error) {
    for query != "" {
       var key string
       key, query, _ = strings.Cut(query, "&")
       if strings.Contains(key, ";") {
          err = fmt.Errorf("invalid semicolon separator in query")
          continue
       }
       if key == "" {
          continue
       }
       key, value, _ := strings.Cut(key, "=")
       key, err1 := QueryUnescape(key)
       if err1 != nil {
          if err == nil {
             err = err1
          }
          continue
       }
       value, err1 = QueryUnescape(value)
       if err1 != nil {
          if err == nil {
             err = err1
          }
          continue
       }
       m[key] = append(m[key], value)
    }
    return err
}

首先通过strings.Cut,将原本的firstname=si&lastname=li,切出一个firstname=si,剩余字符重新保留,再通过strings.Cut将切出firstname=si再次切成firstname以及si。

QueryUnescape的作用是将十六进制数解码,如果传输的是中文,key和value全是十六进制。

最后将value放在对应的key的切片中,这也实现了参数重名的多值传输解析。

相关推荐
行百里er7 小时前
Redis 完结篇——Redis 热点问题在 Spring Boot 项目中实战
redis·后端
高频因子挖掘机8 小时前
只取最近 60 个交易日,却每次拉全历史:量化脚本变慢的原因与改法
后端·github·api
孟健8 小时前
从5000亿Token到83%字节匹配:AI逆向工程与重构的工程闭环实践
后端·架构·ai编程
码事漫谈9 小时前
丢得静悄悄,谈一谈 Agent 的记忆是怎么没的
后端
迪飞特科技9 小时前
智慧物业平台实战:Spring Boot 整合小程序实现报修巡检费用全流程
spring boot·后端·小程序
考虑考虑9 小时前
@JsonInclude注解使用
spring boot·后端·spring
IT_陈寒10 小时前
Vite的这个问题坑了我三天,原来如此解决!
前端·人工智能·后端
网易云信10 小时前
大模型能上岗做销售吗?我们让 12 款国产模型交了 420 份作业
前端·后端
CCblandy10 小时前
HTTP 长连接 3 类故障实测:[WinError 10053]、99 个 TIME_WAIT、69 次多余握手,4 种对策怎么选
后端·python·网络协议·http·云原生·性能优化·架构
蜗牛互联网10 小时前
Python Agent工具调用的白名单、参数校验与审批门禁
java·服务器·前端·人工智能·后端·python