应使用 url.QueryEscape/url.QueryUnescape 处理查询参数,url.PathEscape/url.PathUnescape 处理路径段;二者不可混用,且必须配对解码并检查错误,避免手动拼接或替换。

Go 里做 URL 编解码,别用 fmt.Sprintf 拼接,也别自己写百分号替换逻辑 —— 直接上 url.QueryEscape 和 url.QueryUnescape,它们专为 query string 设计;如果要编码整个 URL 路径(比如路径段含斜杠),得用 url.PathEscape 和 url.PathUnescape,否则会把 / 错误转成 %2F。
query 参数编码:用 url.QueryEscape,不是 url.PathEscape
URL 查询参数(?key=value&name=hello+world)里的值必须对空格、等号、&、中文等做编码,但保留 /、?、= 等分隔符原义 —— 这正是 url.QueryEscape 的职责。
常见错误是拿 url.PathEscape 去处理 query 值,结果把空格变成 %20 是对的,但把 / 也变成 %2F,而 query 中的 / 本不该被编码(除非它属于值本身且语义需要)。
-
url.QueryEscape("a/b c")→"a%2Fb+c"(空格变+,/变%2F,符合 query 规范) -
url.PathEscape("a/b c")→"a%2Fb%20c"(空格变%20,/也变%2F,适合路径段) - 拼接 query 时务必先 escape 值,再组合:
"?name=" + url.QueryEscape(name),不能fmt.Sprintf("?name=%s", name)
URL 路径段编码:用 url.PathEscape,避开 %2F 被二次解析
如果你要把用户输入的文件名、ID 或任意字符串塞进 URL 路径(如 /api/user/张三),必须用 url.PathEscape。它会把所有非 ASCII 和保留字符(包括 /、:、@)都编码,确保路径结构不被破坏。
立即学习“go语言免费学习笔记(深入)”;
容易踩的坑:用 QueryEscape 处理路径段,导致 / 被编码成 %2F,某些反向代理或路由框架(如 Gin)默认不解码 %2F,结果匹配不到路由;更糟的是,有些服务又会自动解码一次,引发歧义。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
url.PathEscape("user/张三")→"user%2F%E5%BC%A0%E4%B8%89"(整个字符串按路径语义编码) -
url.PathEscape("foo:bar")→"foo%3Abar"(冒号也被编码) - 解码必须配对使用
url.PathUnescape,不能混用QueryUnescape,后者会把+当空格,而路径中没有+表示空格的约定
解码失败怎么办?检查错误,别忽略 url.PathUnescape 的返回 err
url.QueryUnescape 和 url.PathUnescape 都返回 (string, error),错误通常是 malformed input:比如孤立的 %、无效的十六进制(%xz)、UTF-8 字节序列不合法等。
生产环境里,用户可能故意传畸形编码串来试探或攻击,忽略错误会导致后续 panic 或逻辑错乱(例如解码出半截 UTF-8 字符)。
- 永远检查 err:
decoded, err := url.PathUnescape(encoded); if err != nil { return err } -
url.PathUnescape("%E5%BC%A0%F0")会失败,因为%F0开头的 UTF-8 四字节序列缺少后续字节 - 不要用
strings.ReplaceAll或正则“手动修复”编码串 —— 解码器本身已足够健壮,干预只会引入新 bug
HTTP 客户端发请求时,别自己拼 URL —— 交给 url.URL 构建
手动拼接 http://host/path?k=v 容易漏掉编码,也难维护。正确做法是用 url.URL 结构体 + url.Values:
u := &url.URL{
Scheme: "https",
Host: "api.example.com",
Path: "/v1/search",
}
q := u.Query()
q.Set("q", "golang 编码") // 自动调用 QueryEscape
q.Set("page", "1")
u.RawQuery = q.Encode() // 注意是 RawQuery,不是 Query
// 最终 u.String() == "https://api.example.com/v1/search?q=golang+%E7%BC%96%E7%A0%81&page=1"
这个模式天然隔离了路径编码和 query 编码,且 q.Set 内部调用的就是 QueryEscape,安全又清晰。如果路径段来自变量,先 PathEscape 再赋给 u.Path。
真正复杂的地方在于:同一个字符串在不同上下文(query / path / fragment)要套不同的编码规则,而且解码时必须严格对应 —— 没有“通用 URL 编码函数”,选错就等于埋雷。

















