第 2 课
百分号编码规则
`%XX` 如何表示字节,以及哪些字符通常需要编码。
百分号编码用 % 加两个十六进制数字(各一位半字节)表示要被转义的 单个字节:
字节 32(空格)→ %20
字节 38(&) → %26
字节 231 → 十六进制 E7 → %E7(语义取决于后续如何解码!)
A–F 不分大小写(%e7 与 %E7 等价)。实现上应尽量风格一致,%XX 大写常用于可读性和差异比对,但解析器理应兼容混用。
一次转义对应一字节(不是 UTF-16 码元)
编码 Unicode 字符串 时,一般先把字符串序列化为 UTF-8 字节,再对每个需要转义的字节写 %HH:
符号 π(U+03C0)
UTF-8 字节序列:CF 80 → %CF%80
错误心智:直接用码点写成某种「03C0 的魔法十六进制」
正确心智:先得到字节序列,再按字节做 % 转义
哪些字节通常要考虑转义
教学向的实用清单:
- 在 数据结构意义上属于数据的字节,但它的图形字符会像 URI 标点那样参与解析(
?、&、#、/、:、@、[、]等)。 - 非 ASCII,为扩大互操作性往往整段稳妥转义路径或查询值中的多字节序列。
- 控制字节、空格等作为「字面含义」出现时,也需转义以避免被当作分隔或被中间层改写。
RFC 3986 规定通用语法与规范化建议;个别框架可能还会禁止残缺的 %,或对某些字符施加更严格的策略。
无效转义示例
不完整或非法的组合会让解析失败或产生安全隐患:
% → 缺少两位十六进制(畸形)
%AB → 只有一位十六进制(畸形)
%GG → G 不是合法十六进制数位
健壮的库通常在构造阶段校验,或对畸形输入报错,而不是静默「猜一个字节」。
过度编码与二次编码
重复执行百分号编码是常见陷阱:
原始取值:100%
第一次: 100%25 (字面量 % 变为 %25)
误再编: 100%2525 (把已转义的 % 再次编码)
若网关或框架做 多次解码,%2525 可能最终变成单个 %,与展示层预期不符。团队应约定:某一层输入到底是「已编码」还是「已解码」,并保持单一责任。
规范化提示
实现之间常见差异包括:
- 十六进制字母大小写(两种都合法)。
- 对于某部件中 法律上可省略转义 的标点,是保留字面还是统一转义。
- 是否在编码前将 Unicode 规范化为 NFC(可选,可减少「拼写不同但视觉相同」造成的签名或缓存键分裂)。
在 CDN、应用、数据库与日志管道之间保持 一致的编码/解码策略,能显著减少验签失败与缓存命中异常。