在我们日常使用互联网的过程中,当点击链接、提交表单或调用 API 时,一个看不见的过程正在默默保障着信息的准确传递 —— 这就是 URL 编码。这个看似简单的技术细节,却是维持 HTTP 协议正常运转的核心机制之一。理解 URL 编码的原理与实践,对于开发者而言不仅能避免常见的技术陷阱,更能深刻把握互联网通信的底层逻辑。
1. URL 编码
1.1. 定义
URL 编码,又称百分号编码(Percent-Encoding),是一种将 URL 中不符合规范的字符转换为特定格式的编码方式。其诞生的直接原因是 URL 对字符集的严格限制 —— 早期的 URL 设计仅支持 ASCII 字符集中的部分字符,这就导致非 ASCII 字符(如中文、日文)和部分特殊符号无法直接在 URL 中使用。
1994 年发布的 RFC 1738 首次规范了 URL 的格式,明确规定了哪些字符可以直接使用,哪些需要编码。随着互联网的发展,2005 年发布的 RFC 3986 对 URL 编码规则进行了更新,成为目前遵循的主要标准。
1.2. 编码规范
下面是一个在参数值中存在特殊字符的报错信息:
1 | Invalid character found in the request target [getInfo?geom=117.054824,36.665425|117.054824,36.66542]. The valid characters are defined in RFC 7230 and RFC 3986 |
报错信息中提到了当前使用的两套规范RFC 3986和RFC 7230,这是互联网中关于URL和HTTP协议的两个核心标准,直接影响URL的格式规范和HTTP请求的处理方式。
1.2.1. RFC 3986:统一资源标识符(URI)规范
发布时间:2005年1月
核心作用:定义URI的语法结构和编码规则,是所有URL必须遵循的基础标准。
URI的基本组成
1 | scheme:[//[user:password@]host[:port]][/path][?query][#fragment] |
例如:
1 | https://user:pass@example.com:8080/api/users?name=john#section1 |
1.2.2. RFC 7230:HTTP/1.1消息语法和路由
发布时间:2014年6月
核心作用:定义HTTP消息的格式、URI解析规则,以及服务器对请求的处理方式。
HTTP请求行格式
1 | METHOD SP request-target SP HTTP-version CRLF |
例如:
1 | GET /api/users?name=john HTTP/1.1 |
2. 字符分类
2.1. 未保留字符(Unreserved)
无需编码,包括:
1
A-Z a-z 0-9 - . _ ~
2.2. 保留字符(Reserved Characters)
这些字符在URL中有特殊用途,作为分隔符时不需要编码,但作为参数值时必须编码:
1
: / ? # [ ] @ ! $ & ' ( ) * + , ; =
字符 | 编码后 | 说明 |
---|---|---|
: |
%3A |
用于分隔方案(如https: )、用户信息(如user:pass )、端口(如:8080 ) |
/ |
%2F |
路径分隔符(如/api/users ) |
? |
%3F |
查询字符串开始标记(如?param=value ) |
# |
%23 |
片段标识符(如#section ) |
[ |
%5B |
方括号(用于IPv6地址等) |
] |
%5D |
方括号 |
@ |
%40 |
用户信息分隔符(如user@domain.com ) |
& |
%26 |
查询参数分隔符(如param1=v1¶m2=v2 ) |
= |
%3D |
参数名与值的分隔符(如key=value ) |
+ |
%2B |
URL中加号通常表示空格,但作为普通字符时需编码 |
$ |
%24 |
用于金融或特殊协议 |
, |
%2C |
用于参数列表分隔 |
2.3. 不安全字符
这些字符可能在传输或解析时导致问题,必须编码:
字符 | 编码后 | 说明 |
---|---|---|
空格 | %20 |
空格在URL中非法,通常编码为%20 或+ (表单编码中) |
" |
%22 |
引号可能导致解析歧义 |
< |
%3C |
HTML标签起始符,可能导致XSS攻击 |
> |
%3E |
HTML标签结束符 |
# |
%23 |
片段标识符,若出现在参数中需编码 |
% |
%25 |
百分比符号本身需要编码(因用于编码其他字符) |
{ |
%7B |
花括号 |
} |
%7D |
花括号 |
` | ` | %7C |
\ |
%5C |
反斜杠 |
^ |
%5E |
脱字符 |
~ |
%7E |
波浪号(通常安全,但某些系统可能需要编码) |
2.4. 非ASCII字符
任何不在 US-ASCII字符集(即0-127
范围外的字符)都必须编码:
- 中文:如
你好
→%E4%BD%A0%E5%A5%BD
- 其他语言:如
é
→%C3%A9
- 特殊符号:如
©
→%C2%A9
3. 编码规则与应用
URL 编码是可逆的过程:
- 编码:字符 → 字节 → %XX 格式。
- 解码:%XX 格式 → 字节 → 字符(根据相同字符集还原)。以上示例是在js中将汉字 “中” 字的字节转换为十六进制,每个字节编码前加%后就变为
1
2
3
4
5
6const encoder = new TextEncoder()
const bytes = encoder.encode('中')
console.log(bytes, 'bbbb')
for (const byte of bytes) {
console.log(byte.toString(16)) // e4 b8 ad
}%E4%B8%AD
。
在不同的编程语言中,实现 URL 编码的方式略有差异,但核心逻辑一致;
3.1. JS编码
JavaScript 中则提供了encodeURIComponent()和encodeURI()两个函数,前者用于编码参数值,后者用于编码完整 URL(不会编码协议、域名等部分的特殊字符):
encodeURI编码:
仅编码 URL 中的特殊字符(如:、/、?、#等),保留 URL 结构字符。
1 | encodeURI('https://restapi.amap.com/v3/distance?origins=116.481028,39.989643|114.481028,39.989643|115.481028,39.989643&destination=114.465302,40.004717') |
encodeURIComponent编码
会编码所有非安全字符(包括 URL 结构字符),适用于编码完整组件。
1 | encodeURIComponent('https://restapi.amap.com/v3/distance?origins=116.481028,39.989643|114.481028,39.989643|115.481028,39.989643&destination=114.465302,40.004717') |
3.2. Java编码
在 Java 中进行 URL 编码主要使用java.net.URLEncoder
和java.net.URLDecoder
类。
1 | String url = "https://restapi.amap.com/v3/distance?origins=116.481028,39.989643%7C114.481028,39.989643%7C115.481028,39.989643&destination=114.465302,40.004717"; |