Go 中 url.Values 构建查询参数

FreeGuideOnline 最新 2026-07-07

go package main

import ( "fmt" "net/url" )

func main() { v := url.Values{ "name": []string{"小明"}, "age": []string{"28"}, "hobby": []string{"coding", "reading"}, } fmt.Println(v.Encode()) // 输出: age=28&hobby=coding&hobby=reading&name=%E5%B0%8F%E6%98%8E }


注意每个 key 对应的值是字符串切片,即使只有一个值也必须使用切片格式,这符合 HTTP 协议中同一参数可以多次出现的规定。

### 使用 make 初始化空 Values

当参数需要动态添加时,先创建一个空的 `Values`,再通过方法追加参数。

```go
v := make(url.Values)
// 或
v := url.Values{}

之后可以用 AddSet 方法添加数据。

核心方法:添加与设置参数

Values 提供了三个常用方法:AddSetDel,它们的方法签名都很简洁。

Add 方法:追加参数值

Add(key, value string) 会将一个新值追加到指定 key 的列表末尾,不会覆盖已有值。

v := url.Values{}
v.Add("lang", "go")
v.Add("lang", "python")
fmt.Println(v.Encode()) // lang=go&lang=python

Set 方法:设置参数值

Set(key, value string) 会清空 key 当前的所有值,然后设置为你传入的单个值。当你希望参数只出现一次时,应使用 Set

v := url.Values{}
v.Set("page", "1")
v.Set("page", "2") // 覆盖前一个值
fmt.Println(v.Encode()) // page=2

Del 方法:删除参数

Del(key string) 用于删除某个参数及其所有值。

v := url.Values{"token": []string{"abc123"}}
v.Del("token")
fmt.Println(v.Encode()) // 空字符串

Get 方法:获取第一个值

Get(key string) 返回该参数的第一个值,如果不存在则返回空字符串。这在绝大多数场景下已经够用。

v := url.Values{"sort": []string{"price", "rating"}}
fmt.Println(v.Get("sort")) // price

如果需要获取全部值,直接使用 v[key] 即可。

将 url.Values 编码为查询字符串

调用 Encode() 方法即可将参数编码为标准查询字符串形式。该方法会自动排序 key(按字典序),并对 key 和 value 进行正确的 URL 编码(百分号编码),特殊字符和多字节字符都会被处理。

v := url.Values{}
v.Add("search", "Golang 教程")
v.Add("category", "编程&开发")
fmt.Println(v.Encode())
// category=%E7%BC%96%E7%A8%8B%26%E5%BC%80%E5%8F%91&search=Golang+%E6%95%99%E7%A8%8B

注意:Encode() 产生的字符串格式为 key=value&key=value,与 ? 之后的查询字符串完全一致,你可以直接拼接到 URL 中。

实际应用:构造完整 URL

最常见的用法是将 Values 作为查询参数附加到基础 URL 上。

u, err := url.Parse("https://api.example.com/data")
if err != nil {
    panic(err)
}

q := u.Query()          // 获取已有查询参数(返回 url.Values)
q.Set("page", "2")
q.Add("limit", "20")
u.RawQuery = q.Encode() // 将编码后的字符串设置回去

fmt.Println(u.String())
// https://api.example.com/data?limit=20&page=2

如果基础 URL 本身已带查询参数,u.Query() 会解析它们,你可以在此基础上修改或新增。

如果需要从头构建,可以直接拼接:

base := "https://example.com/search"
v := url.Values{"q": []string{"golang url values"}}
fullURL := base + "?" + v.Encode()

但更推荐使用 url.URL 结构体,因为 RawQuery 会自动处理编码和特殊字符。

从查询字符串解析回 Values

反向操作同样简单,使用 url.ParseQuery 可以将查询字符串解析为 Values

rawQuery := "name=张三&age=25&hobby=篮球&hobby=旅行"
values, err := url.ParseQuery(rawQuery)
if err != nil {
    panic(err)
}
fmt.Println(values.Get("name"))   // 张三
fmt.Println(values["hobby"])      // [篮球 旅行]

这个方法会自动处理百分号解码,比手动 strings.Split 安全得多。

常见错误与注意事项

1. 值类型必须是字符串切片

直接给 http 请求的 Form 字段赋值时,请确保类型为 url.Values

// 错误示范
data := map[string]string{"key": "val"}
req.Form = url.Values(data) // 编译错误

正确做法是使用循环转换,或直接使用 url.Values 字面量。

2. Encode() 后不要重复编码

Encode() 已经进行了 URL 编码,如果你再次使用 url.QueryEscape 对整体编码,会导致双重编码,参数被破坏。

3. 区分 Add 和 Set 的语义

在一个典型的分页 API 中,page 参数通常只需要一个值,应使用 Set;而像 tag 这样的过滤器可能需要多个值,可以使用 Add。混用可能导致意外的重名参数。

进阶技巧:构造带数组的参数

某些 API 接受形如 ids=1&ids=2ids[]=1&ids[]=2 的参数。使用 Values 可以轻松实现:

v := url.Values{}
v["ids"] = []string{"11", "22", "33"}
// 或
v.Add("ids[]", "11")
v.Add("ids[]", "22")