你可能遇到过这种情况:前端明明把查询参数拼好了,后端却收到乱码;或者 URL 里混入 +、空格、中文后,API 就开始返回 400。这类问题通常不是网络故障,而是 URL 编码(URL Encoding) 没有在正确时机、对正确区段处理。
一、URL 编码是什么?为什么必须做?
URL 原本只能安全传递有限字符(字母、数字和少量符号)。当你要在 URL 中放入空格、中文、emoji,或像 &、= 这样的结构符号时,必须先做百分比编码(Percent-Encoding),格式为 % 加两位十六进制值,例如空格常见写法是 %20。
如果不编码,解析器会把内容当成结构分隔符,造成参数截断、值错位,甚至被注入额外参数。
二、哪些字符需要编码?
实务上建议按三类理解:
- Unreserved(通常无需编码):
A-Z、a-z、0-9、-、_、.、~ - Reserved(有结构语义):如
:、/、?、#、[、]、@、&、= - 非 ASCII 字符:中文、日文、韩文、emoji 等,需要先转 UTF-8 再做百分比编码
重点不是“全部都编码”,而是“在正确区段里,只编码该编码的部分”。
三、为什么空格有时是 %20,有时是 +?
这是最常见的混淆点。在普通 URL 百分比编码中,空格通常表示为 %20。但在 application/x-www-form-urlencoded(如 HTML 表单提交)规则中,空格会转换为 +。
如果系统把 + 当成字面值而不是空格,就会出现数据不一致。前后端需要先统一编码规范。
四、JavaScript:encodeURI 与 encodeURIComponent 的区别
两者都能编码,但用途不同:
encodeURI:用于整段 URL,会保留:、/、?等结构符号。encodeURIComponent:用于单个参数值,会把&、=等也编码,避免污染查询结构。
const keyword = 'C++ 教程 & 示例';
const url = '/search?q=' + encodeURIComponent(keyword);
// /search?q=C%2B%2B%20%E6%95%99%E7%A8%8B%20%26%20%E7%A4%BA%E4%BE%8B
如果你把参数值交给 encodeURI,& 很可能被错误识别成新参数分隔符。
五、常见错误:重复编码(Double Encoding)
重复编码通常发生在“前端先编码一次,后端或 SDK 又编码一次”。结果是原本的 % 被编码成 %25,数据看起来像被破坏。
- 原始:
hello world - 一次编码:
hello%20world - 二次编码:
hello%2520world
建议在接口文档中明确“谁负责编码、谁负责解码”,避免重复处理。
六、Path、Query、Fragment 要分开处理
URL 各区段规则不同。常见错误是把整段 URL 当成同一字符串处理,导致路径斜杠被误编码,或查询参数漏编码。
| 区段 | 建议做法 | 常见错误 |
|---|---|---|
| Path | 按段编码,保留路由结构 | 把 /a/b 整段编码导致路由失效 |
| Query | 键和值分别编码 | &、= 未编码导致参数拆裂 |
| Fragment | 按前端路由规则处理 | 与后端查询参数混用编解码 |
七、安全视角:编码不是防御本身
URL 编码只能保证传输格式正确,不等于防 XSS 或 SQL 注入。它属于“表示层处理”,不是“输入校验”或“输出转义”的替代方案。
- 接收端仍需白名单校验和长度限制。
- 输出到 HTML、SQL、Shell 时要使用对应场景的转义机制。
- 不要把解码后的内容直接插入 DOM。
总结
URL 编码的关键不是死记字符表,而是建立可预测的协作流程。只要做到“区段分离、职责单一、规范对齐”,编码问题就会从难查 bug 变成可快速定位的工程细节。