URL 编码解码完全指南:百分号编码原理

URL 编码(也称"百分号编码",Percent-Encoding)是 Web 开发中最基础却最容易出错的知识之一。无论是构建 API 请求、处理表单提交,还是调试网络请求,理解 URL 编码都至关重要。

为什么需要 URL 编码?

URL(统一资源定位符)的规范(RFC 3986)规定,URL 中只能包含特定的 ASCII 字符。当 URL 中需要包含中文、空格、特殊符号等非法字符时,必须通过百分号编码将其转换为合法格式。

// 未编码的 URL(包含中文和空格,是无效的)
https://example.com/search?q=你好 世界&lang=中文

// 编码后的合法 URL
https://example.com/search?q=%E4%BD%A0%E5%A5%BD%20%E4%B8%96%E7%95%8C&lang=%E4%B8%AD%E6%96%87

百分号编码规则

百分号编码的过程是:将字符转换为 UTF-8 字节序列,每个字节表示为 % 加两位十六进制数。

字符    UTF-8 字节     编码结果
空格    0x20          %20
!       0x21          %21
#       0x23          %23
你      0xE4BDA0      %E4%BD%A0
好      0xE5A5BD      %E5%A5%BD

不需要编码的字符

以下字符在 URL 中可以直接使用,无需编码:

  • 非保留字符A-Z a-z 0-9 - _ . ~

保留字符

保留字符在 URL 中有特殊含义,作为数据使用时必须编码:

字符URL 中的含义编码
:协议分隔符%3A
/路径分隔符%2F
?查询字符串开始%3F
#Fragment 开始%23
&参数分隔符%26
=键值分隔符%3D
+空格(表单编码)%2B
@用户信息分隔%40

JavaScript 编码函数对比

JavaScript 提供了多个 URL 编码函数,它们的行为有重要区别:

encodeURIComponent(推荐用于参数值)

编码除了 A-Z a-z 0-9 - _ . ! ~ * ' ( ) 之外的所有字符。适合编码查询参数的键或值。

encodeURIComponent("name=张三&age=25")
// "name%3D%E5%BC%A0%E4%B8%89%26age%3D25"

// 正确用法:编码参数值
const url = `https://api.example.com/search?q=${encodeURIComponent("你好 世界")}`;
// "https://api.example.com/search?q=%E4%BD%A0%E5%A5%BD%20%E4%B8%96%E7%95%8C"

encodeURI(用于完整 URL)

不编码 URL 的结构字符(: / ? # [ ] @ ! $ & ' ( ) * + , ; =)。适合编码完整 URL。

encodeURI("https://example.com/路径?q=你好")
// "https://example.com/%E8%B7%AF%E5%BE%84?q=%E4%BD%A0%E5%A5%BD"

// 注意:? 和 = 没有被编码,保留了 URL 结构
⚠️ 常见错误:encodeURI 编码参数值会导致 &= 不被编码,破坏查询字符串结构。参数值始终用 encodeURIComponent

函数对比速查表

输入encodeURIComponentencodeURI
hello worldhello%20worldhello%20world
a=1&b=2a%3D1%26b%3D2a=1&b=2
你好%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD
/path?q=1%2Fpath%3Fq%3D1/path?q=1

表单编码(application/x-www-form-urlencoded)

HTML 表单提交使用的编码方式与标准百分号编码略有不同:

  • 空格编码为 + 而非 %20
  • 键值对用 & 连接
// URLSearchParams 自动处理表单编码
const params = new URLSearchParams();
params.append("name", "张 三");
params.append("city", "北京");
console.log(params.toString());
// "name=%E5%BC%A0+%E4%B8%89&city=%E5%8C%97%E4%BA%AC"
// 注意空格变成了 + 号

其他语言的 URL 编码

Python

from urllib.parse import quote, unquote, urlencode

# 编码单个值
quote("你好 世界")
# '%E4%BD%A0%E5%A5%BD%20%E4%B8%96%E7%95%8C'

# 编码查询参数
urlencode({"name": "张三", "age": "25"})
# 'name=%E5%BC%A0%E4%B8%89&age=25'

# 解码
unquote("%E4%BD%A0%E5%A5%BD")
# '你好'

Java

// Java 11+
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

String encoded = URLEncoder.encode("你好 世界", StandardCharsets.UTF_8);
// "%E4%BD%A0%E5%A5%BD+%E4%B8%96%E7%95%8C"  (注意空格是+)

String decoded = URLDecoder.decode(encoded, StandardCharsets.UTF_8);
// "你好 世界"
💡 调试技巧:遇到 URL 编码问题时,打开浏览器开发者工具的 Network 面板,查看请求的 URL 和参数。大多数工具会自动显示解码后的参数,方便调试。

常见问题

双重编码

对已经编码的字符串再次编码,会导致 % 被编码为 %25

encodeURIComponent("%E4%BD%A0")
// "%25E4%25BD%25A0"  ← 双重编码,导致乱码!

空格的表示

空格在 URL 中有两种表示:%20(路径中)和 +(表单查询参数中)。使用 encodeURIComponent 会产生 %20,使用 URLSearchParams 会产生 +

🛠️ 在线 URL 编码解码工具

打开 URL 工具 →

延伸阅读