Golang Gin 实战Ⅳ | URL查询参数的获取和原理分析

260 阅读3分钟

查询参数(query parames)

Querystring parameters ,翻译成中文我只能叫查询参数了,不过觉得挺别捏的。其实这种参数我们并不陌生,比如:

https://www.coolpest8.com/search?q=golang&wechat=zb13161658867

URL查询参数,或者也可以简称为URL参数,是存在于我们请求的URL中,以?为起点,后面的k=v&k1=v1&k2=v2这样的字符串就是查询参数,比如我上面示例中的:

?q=golang&wechat=zb13161658867

这个示例中有两个查询参数键值对:

q=golang
wechat=zb13161658867

第一个key是q,对应的值是golang。第二个key是wechat,对应的值是zb13161658867,它们通过&相连。在URL中,多个查询参数键值对通过&相连。

Gin获取查询参数

Gin中,为我们提供了简便的方法来获取查询参数的值,我们只需要知道查询参数的key(参数名)就可以了。

func main() {
r := gin.Default()

r.GET("/", func(c *gin.Context) {
	c.String(200, c.Query("wechat"))
})
r.Run(":8080")
}

我们运行这段代码,打开浏览器访问http://localhost:8080/?wechat=zb13161658867,就可以看到zb13161658867文字。这表示我们通过c.Query("wechat")获取到了查询参数wechat的值是zb13161658867。

Query方法为我们提供了获取对应key的值的能力,如果该key不存在,则返回""字符串。如果对于一些数字参数,比如id如果返回为空的话,我们进行字符串转数字的时候会报错,这时候,我们就可以通过DefaultQuery方法指定一个默认值:

c.DefaultQuery("wechat", "zb13161658867")
c.DefaultQuery("id", "0")

比如这样,尤其是第二个例子,默认为0,让我们字符串转数字很方便。

func (c *Context) Query(key string) string {
value, _ := c.GetQuery(key)
return value
}

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

看下这两个函数的源代码实现,它们都是调用的GetQuery方法获取对应的值,唯一不同的是DefaultQuery会判断对应的key是否存在,如果不存在的话,则返回默认defaultValue值。

原理解析

从以上两个获取查询参数值的方法可以看到,他们调用的都是GetQuery,这也是gin.Context的一个方法,它和Query唯一不同的是,它返回两个值,可以告诉我们要获取的key是否存在。

value, ok := c.GetQuery("wechat")

如果我们自己的业务中,需要这类功能,可以用GetQuery来代替Query方法。

GetQuery方法的底层实现其实是c.Request.URL.Query().Get(key),通过url.URL.Query()来获取所有的参数键值对。

本质上是调用的GetQueryArray,取的数组中第一个值
func (c *Context) GetQuery(key string) (string, bool) {
    if values, ok := c.GetQueryArray(key); ok {
        return values[0], ok
    }
    return "", false
}

func (c *Context) GetQueryArray(key string) ([]string, bool) {
    c.getQueryCache() //缓存所有的键值对
    if values, ok := c.queryCache[key]; ok && len(values) > 0 {
        return values, true
    }
    return []string{}, false
}

func (c *Context) getQueryCache() {
    if c.queryCache == nil {
        c.queryCache = c.Request.URL.Query()
    }
}

从以上的实现代码中,可以看到最终的实现都在GetQueryArray方法中,找到对应的key就返回对应的[]string,返回就返回空数组。

这里Gin进行了优化,通过缓存所有的键值对,提升代码的查询效率。这里缓存的queryCache本质上是url.Values,也是一个map[string][]string。 type Values map[string][]string 其中c.Request.URL.Query()这个方法就是把?k=v&k1=v1&k2=v2这类查询键值对转换为map[string][]string,所以还是很耗性能的,这里Gin采用了缓存的做法提高了性能挺好,这也是Gin成为性能最快的Golang Web 框架的原因之一吧。