第 5 课
表单中的加号问题
为何 `+` 有时表示空格,以及 `%20` 与 `+` 的差异。
默认的 HTML <form method="GET"> 会把字段序列化成 application/x-www-form-urlencoded 风格:空格常被写成 +(出现在提交后的 URL query 片段里很常见):
<form>
<input name="msg" value="hello world">
</form>
<!-- 提交后可能出现:?msg=hello+world -->
这与你在 任意 URI 解析器 里遇到的规则 未必相同。有的实现把 query 当成纯 URI component:此时 + 就是字面加号,只有按 表单规则解析时,+ 才会被解释为 ASCII 空格。
?msg=a+b // 在「表单语义」里常解读为「a」+空格+「b」;在「纯 URI」语义里可能只是三个字符 a,+ ,b
?q=city%20hall // 明确表示 ASCII 空格,多数 URI 语义下更不易歧义
JavaScript 中的典型行为
URLSearchParams
const p = new URLSearchParams("a=b+c");
console.log(p.get("a")); // 常按表单习惯把 '+' 当作空格解码
当键值里 真的需要字面加号(例如算术式、Base64、A+B) 时,构建阶段应改用 %2B,避免在中间层被误换。
const usp = new URLSearchParams();
usp.set("expr", "1+2");
console.log(usp.toString()); // 具体输出可能因实现细节略有差异,但关键是:「+」是否被当作格式字符
只用 decodeURIComponent 的误区
decodeURIComponent 只处理 %HH,不会把 + 变成空格:
decodeURIComponent("a+b"); // 仍是 "a+b"
// 只有当你明确处于「表单解码层」时,才可能先做:
"a+b".replace(/\+/g, " ");
把这两步混在同一层,很容易在网关 / 应用之间产生 二次替换 或 该换没换。
服务端与中间件
各语言框架常会区分:
- 「querystring 原始解析」;
- 「
application/x-www-form-urlencoded解析(含+→ 空格)」。
流水线顺序颠倒时,常见问题包括:令牌、签名载荷里的 + 被静默改写,staging 与 production 解析模式不一致。
实用对照
| 目标 | 建议 |
|---|---|
| 字面量必须是加号并通过 URL/query 传输 | 在取值层写成 %2B |
| 复杂结构化数据 | 优先考虑 JSON 请求体,而不是把一切塞进查询串 |
| 线上排障 | 同时记录 ? 后面的原始字节 与各层解析后的对象 |
弄清 + 属于 表单编码约定而非「URI 天生如此」,就能把不少「环境里正常、环境里挂」的差异定位到具体的解析语义上;并与第 4 课强调的 分界与分层编码 一起使用。