为什么要编码?
如果一样东西需要编码,那说明它不适合传输,原因有多种(有些字符有歧义、包含隐私数据、Size过大)。
在 URL 中,之所以要编码,是因为有些字符会引起歧义。比如参数中的key=value键值对,当value里含有= & ? 等,就会造成 URL 服务器的解析错误。所以必须将引起歧义的符号进行转义,也就是对其进行编码。
URL 的编码格式采用的是 ASCII 码,而不是 Unicode,也就是说 URL 中不能包含任何非 ASCII 字符,比如中文(如果客户端浏览器和服务器端浏览器支持的字符集不同,中文可能会造成问题,现在一般默认都使用 UTF-8)。
URI的编码原则是使用安全的字符去(没有特殊用途和特殊意义的可打印字符)表示那些不安全的字符。URI 是统一资源标识,我们所说的 URL 只是 URI 的一种。
哪些字符需要编码?
RFC3986文档规定:URL 中只允许包含英文字母(a-zA-Z)、数字(0-9)、4个特殊字符(-_.~)、所有保留字符。
保留字:URL 可以划分成若干组件,协议、主机、路径等
一些字符用来分隔不同组件,比如 :/?#@
一些字符在每个组件中分隔的,比如 &=
不安全字符:还有些字符,当它们直接放在 URL 中时,可能会引起解析程序的歧义,原因很多。
1.空格 URL在传输过程 用户排版时 文本处理程序在处理URL的过程 都有可能引入无关紧要的空格;或者将有意义的空格给去掉
2.引号和<> 通常用于在普通文本中起到分隔URL的作用
3.# 通常用于标示书签或锚点
4.% 它本身被用作对不安全字符编码时的特殊字符,所以本身需要编码
5.{}[]|^’ 某些网关或者传输代理会篡改这些字符
RFC3986文档对URL的编码问题做出了详细的建议,指出哪些字符需要被编码才不会引起 URL 语义的转变,以及为什么对这些字符进行编码做出了解释。RFC3986指定了以下字符为保留字 :/?=&#@+,;()$’*![]
需要注意:
由于历史原因,目前存在一些不标准的编码实现,比如对于符号~ 虽然RFC3986文档规定,对于~不需要编码,但是一些老的网关或者传输代理会。比如[]也被编码了。
对于 URL 中的合法字符,编码和不编码是等价的,但是对于上面提到的那些字符,如果不编码,那它们可能会造成 URL 语义的不同。因此,对于 URL 而言,只有普通英文字符和数字、特殊字符和保留字符,能出现在未经编码的 URL 中,其他字符都需要经过编码。
如何编码?
URL 编码通常被称为百分号编码(URL Encoding, also know as percent-encoding),因为它的编码方式很简单,使用 % 加上两位的字符-代表一个字节的16进制形式。
URL 编码默认使用的字符集是 US-ASCII
比如 a 在US-ASCII中对应的字节是 0x61,那么URL编码之后得到的就是 %61
又如 @ 在ASCII字符集中对应的字节是 0x40,经过URL编码之后得到的是 %40
对于非 ASCII 字符,需要使用 ASCII 字符集的超集进行编码得到相应的字节,然后再对每个字节执行百分号编码。
对于 Unicode 字符,RFC文档建议使用 UTF-8 对其进行编码得到相应的字节,然后再对每个字节执行百分号编码。
JS中的URL编码
JS 提供了 3 对函数来对 URL 编码以得到合法的 URL,它们分别是:
- escape/unescape
- encodeURI/decodeURI
- encodeURIComponent/decodeURIComponent
由于编码和解码是可逆的,所以这里只解释编码过程。
这里只说后两种编码,encodeURI 和 encodeURIComponent,它两都是用 UTF-8 对非 ASCII 字符进行编码,然后再进行百分号编码的。
encodeURI()
目的:对 URI 进行完整编码
所以,它对在 URI 中具有特殊含义的 ASCII 标点符号,它是不会进行转义的,比如以下11个字符 ;/?:@&=+$,#
它不会对以下进行编码:
1.ASCII 字母
2.数字
3.ASCII 标点符号,以下9个 -_.!~*()’ 【单引号不编,双引号会被编码】
4.在 URI 中有特殊含义的 ASCII 标点符号,以下11个 ;/?:@&=+$,#
其他字符会被编码成16进制的转义序列
encodeURIComponent()
目的:把字符串作为 URI 组件进行编码
它不会对以下进行编码:
1.ASCII 字母
2.数字
3.ASCII 标点符号,以下9个 -_.!~*()’ 【单引号不编,双引号会被编码 %22】
ASCII 中的其他字符(用于分隔URI组件的标点符号)们都会被 16进制的转义序列,替换掉。
它们的区别
安全字符不同:
encodeURIComponent() 假定参数是 URI 的一部分(比如协议、主机名、路径、查询字符串),所以,会转义用于分隔 URI 各个部分的标点符号。
所以,如果待编码的字符中含有分隔符 ? & # 等时,则应该用 encodeURIComponent()
适用场合不同:
encodeURI 是对一个完整的 URI 进行编码
encodeURIComponent 是对 URI 的一个组件进行编码
表单提交
当 HTML 表单被提交时,每个表单域都会被 URL 编码之后才发送。
由于历史原因,表单使用的 URL 编码实现并不符合最新的标准,例如空格使用的不是 %20,而是 + 号
参考
http://www.w3school.com.cn/jsref/jsref_encodeuri.asp
http://www.w3school.com.cn/jsref/jsref_encodeURIComponent.asp
http://www.cnblogs.com/greatverve/archive/2011/12/12/URL-Encoding-Decoding.html
RFC3986文档对URL的描述
https://tools.ietf.org/html/rfc3986
16进制的转义序列:
: %3A
/ %2F
? %3F
= %3D
& %26
空格 %20