go语言HTTP GET解析URL中的查询参数
经常我们会使用下面的逻辑来处理查询参数:
func myHandler(w http.ResponseWriter, r *http.Request) {
vars := r.URL.Query();
a := vars["a"][0]
...
}
描述起来很简单,先获取Query对象,然后从Query对象里面根据名字解析到一个参数,再取其中的第一个元素值。
下面我们仔细了解一下这几个数据对象:
Query() 是什么对象
语句 vars := r.URL.Query() 返回的vars是一个什么对象?
Query函数的定义是是这样的:
# net/url/url.go
func (u *URL) Query() Values {
v, _ := ParseQuery(u.RawQuery)
return v
}
// Query is expected to be a list of key=value settings separated by
// ampersands or semicolons. A setting without an equals sign is
// interpreted as a key set to an empty value.
func ParseQuery(query string) (Values, error) {
m := make(Values)
err := parseQuery(m, query)
return m, err
}
// Values maps a string key to a list of values.
// It is typically used for query parameters and form values.
// Unlike in the http.Header map, the keys in a Values map
// are case-sensitive.
type Values map[string][]string
这里我们可以看到Query()返回的Values实际上是一个map对象,这个map的key是字符串,value是字符串数组,正好是参数名到参数值数组的map关系,因为同一个参数名可以有多个值,所有参数值是一个字符串数组。例如:
map[“A”] = [“A1”]
map[“B”] = [“B1”, “B2”]
另外我们从代码看到vars不能为空nil值,虽然vars里面的成员可以为空,也就是说如果URL里面没有查询参数,那么vars的成员为空,即大小为0,但vars本身不为nil。
如何解析参数值
第一种原始方法遍历解析Query对象的成员。
vars := r.URL.Query();
a, ok := vars["a"]
if !ok {
fmt.Printf("param a does not exist\n");
} else {
fmt.Printf("param a value is [%s]\n", a);
}
几个例子:
URL | Result |
---|---|
curl http://localhost:8080/api/service/get | param a does not exist |
curl http://localhost:8080/api/service/get?a | param a value is [[]] |
curl http://localhost:8080/api/service/get?a= | param a value is [[]] |
curl http://localhost:8080/api/service/get?a=a1 | param a value is [[a1]] |
curl http://localhost:8080/api/service/get?a=a2&a=a2 | param a value is [[a1 a2]] |
从上述例子,我们可以看到a的类型是一个数组,即使是没有值,也是一个空数组。
使用Get方法解析参数
Get方法是专门用来处理只有一个参数的参数值,即当参数不存在是返回空串(“”),当参数存在多个值时返回第一个值。
Get方法的定义如下:
// Get gets the first value associated with the given key.
// If there are no values associated with the key, Get returns
// the empty string. To access multiple values, use the map
// directly.
func (v Values) Get(key string) string {
if v == nil {
return ""
}
vs := v[key]
if len(vs) == 0 {
return ""
}
return vs[0]
}
重新验证上面的代码逻辑:
v := r.URL.Query();
a := v.Get("a")
fmt.Printf("param a value is [%s]\n", a);
运行结果
URL | Result |
---|---|
curl http://localhost:8080/api/service/get | param a value is [] |
curl http://localhost:8080/api/service/get?a | param a value is [] |
curl http://localhost:8080/api/service/get?a= | param a value is [] |
curl http://localhost:8080/api/service/get?a=a1 | param a value is [a1] |
curl http://localhost:8080/api/service/get?a=a2&a=a2 | param a value is [a1] |
可以看到如果参数不存在Get方法函数空串,当有多个参数时Get方法返回第一个值。
相比较前面一种方法,Get不需要考虑空数组越界的问题,如果不存在就返回空串即可。