URL
URL 为什么要编码(解析)?
URL
中有些字符会引起歧义。例如URL
参数字符串中使用key=value
键值对这样的形式来传参,键值对之间以&符号分隔。如果你的 value 字符串中又包含了=
或者&
,那么势必会造成接收URL
的服务器解析错误,因此必须将引起歧义的&
和=
符号进行转义,也就是对其进行编码。又如,URL
的编码格式采用的是ASCII
码,而不是Unicode
,这也就是说你不能在URL
中包含任何非ASCII
字符,例如中文。否则如果客户端浏览器和服务端浏览器支持的字符集不同的情况下,中文可能会造成问题。
URL
编码的原则就是使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示那些不安全的字符。
哪些字符需要编码
URL 特殊字符转义,URL 中一些字符的特殊含义,基本编码规则如下:
- 空格换成加号(+)
- 正斜杠(/)分隔目录和子目录
- 问号(?)分隔 URL 和查询
- 百分号(%)制定特殊字符
- #号指定书签
- &号分隔参数
不需要编码的字符:
RFC3986 文档规定,Url 中只允许包含以下四种:
-
英文字母(a-zA-Z)
-
数字(0-9)
-
-_.~ 4 个特殊字符
-
所有保留字符,RFC3986 中指定了以下字符为保留字符(英文字符):
! * ' ( ) ; : @ & = + $ , / ? # [ ]
需要编码的字符:
如果需要在URL
中用到特殊字符,需要将这些特殊字符换成相应的十六进制的值。有一些字符,当他们直接放在URL
中的时候,可能会引起解析程序的歧义。这些字符被视为不安全字符
空格
:Url 在传输的过程,或者用户在排版的过程,或者文本处理程序在处理 Url 的过程,都有可能引入无关紧要的空格,或者将那些有意义的空格给去掉。引号以及<>
:引号和尖括号通常用于在普通文本中起到分隔 Url 的作用#
:通常用于表示书签或者锚点%
:百分号本身用作对不安全字符进行编码时使用的特殊字符,因此本身需要编码{}|\^[]
~`:某一些网关或者传输代理会篡改这些字符
因此对于URL
而言,只有普通英文字符
和数字
、特殊字符$-_.+!*'()
还有保留字符
,才能出现在未经编码的URL
之中。其他字符均需要经过编码之后才能出现在URL
中
如何对 Url 中的非法字符进行编码
URL
编码通常也被称为百分号编码 是因为它的编码方式非常简单,使用%
百分号加上两位的字符——0123456789ABCDEF
——代表一个字节的十六进制形式。URL
编码默认使用的字符集是US-ASCII
。例如a
在US-ASCII
码中对应的字节是0x61
,那么URL
编码之后得到的就是%61
- 对于非
ASCII
字符,需要使用ASCII
字符集的超集进行编码得到相应的字节,然后对每个字节执行百分号编码。 - 对于
Unicode
字符,RFC 文档建议使用utf-8
对其进行编码得到相应的字节,然后对每个字节执行百分号编码。如"中文"使用UTF-8
字符集得到的字节为 0xE4 0xB8 0xAD 0xE6 0x96 0x87,经过 Url 编码之后得到"%E4%B8%AD%E6%96%87"。 - 如果某个字节对应着
ASCII
字符集中的某个非保留字符,则此字节无需使用百分号表示。例如"Url 编码",使用UTF-8
编码得到的字节是 0x55 0x72 0x6C 0xE7 0xBC 0x96 0xE7 0xA0 0x81,由于前三个字节对应着 ASCII 中的非保留字符"Url",因此这三个字节可以用非保留字符"Url"表示。最终的 Url 编码可以简化成"Url%E7%BC%96%E7%A0%81" ,当然,如果你用"%55%72%6C%E7%BC%96%E7%A0%81"也是可以的。
**列举带有特殊字符的参数替换成另一些代替的参数,如下所示 **
字符 - URL编码值
空格 - %20 (URL中的空格可以用+号或者编码值表示)
"
- %22
#
- %23
%
- %25
&
- %26
(
- %28
)
- %29
+
- %2B
,
- %2C
/
- %2F
:
- %3A
;
- %3B
<
- %3C
=
- %3D
>
- %3E
?
- %3F
@
- %40
\
- %5C
|
- %7C
{
- %7B
}
- %7D
具体编码处理方法
JavaScript
中提供了3对函数用来对Url
编码以得到合法的Url
,它们分别是escape / unescape
, encodeURI / decodeURI
和encodeURIComponent / decodeURIComponent
。其中,escape
,encodeURI
,encodeURIComponent
——都是用于将不安全不合法的Url
字符转换为合法的Url
字符表示,它们有以下几个不同点。
-
安全字符不同:
- escape(69个):
*/@+-._0-9a-zA-Z
(即 escape 不会对*/@+-._0-9a-zA-Z
进行编码) - encodeURI(82个):
!#$&'()*+,/:;=?@-._~0-9a-zA-Z
(即encodeURI
不会对!#$&'()*+,/:;=?@-._~0-9a-zA-Z
进行编码) - encodeURIComponent(71个):
!'()*-._~0-9a-zA-Z
(即encodeURIComponent
不会对!'()*-._~0-9a-zA-Z
进行编码)
- escape(69个):
-
兼容性不同:
- escape() 是从Javascript 1.0的时候就存在了
- encodeURI() 在Javascript 1.5才引入的。但是由于Javascript 1.5已经非常普及了,所以实际上使用encodeURI和encodeURIComponent并不会有什么兼容性问题。
- encodeURIComponent 在Javascript 1.5才引入的。但是由于Javascript 1.5已经非常普及了,所以实际上使用encodeURI和encodeURIComponent并不会有什么兼容性问题。
-
对Unicode字符的编码方式不同:
- 这三个函数对于
ASCII
字符的编码方式相同,均是使用百分号+两位十六进制字符来表示。 - 对于
Unicode
字符- **escape()**的编码方式是%uxxxx,其中的xxxx是用来表示unicode字符的4位十六进制字符。这种方式已经被W3C废弃了。但是在ECMA-262标准中仍然保留着escape的这种编码语法。
- **encodeURI()和encodeURIComponent()**则使用
UTF-8
对非ASCII
字符进行编码,然后再进行百分号编码。这是RFC推荐的。因此建议尽可能的使用这两个函数替代escape进行编码。
- 这三个函数对于
-
适用场合不同
- **encodeURI()**被用作对一个完整的URI进行编码
- encodeURIComponent被用作对URI的一个组件进行编码。
encodeURIComponent
编码的字符范围要比encodeURI
的大。
影响 URL 编码结果的因素
Meta charset 属性
-
meta
标签中charset = UTF-8
。规定HTML
文档的字符编码为UTF-8
。如果你的网页里面出现了中文,在头部没有这一句 的话,将会导致中文乱码。 -
meta
标签中charset = gb2312
。规定HTML
文档的字符串编码为gb2312
不同的浏览器
对于包含中文的Url的处理问题,不同浏览器有不同的表现。例如对于IE,如果你勾选了高级设置"总是以UTF-8发送Url",那么Url中的路径部分的中文会使用UTF-8进行Url编码之后发送给服务端,而查询参数中的中文部分使用系统默认字符集进行Url编码。
为了保证最大互操作性,建议所有放到Url中的组件全部显式指定某个字符集进行Url编码,而不依赖于浏览器的默认实现。