URL(Uniform Resource Locator)统一资源定位符,是 URI(Uniform Resource Identifiers)的一种。
URL中参数使用key=value键值对表示,键值对之间以&符号分隔,如/abc?x=1&y=2。如果key或value字符串中包含了=或&,那么就会造成接收URL的服务器解析错误,因此必须对&和=符号进行转义,也就是编码。编码的原则就是使用安全的字符去表示那些不安全的字符。
编码规则
URL根据 RFC 3986 中的规则进行编码。
URL编码通常也被称为百分号编码(percent-encoding),因为编码是使用%百分号加两位十六进制字符(代表一个字节)的形式。URL的编码格式采用的是ASCII码,即URL中不能包含任何非ASCII字符,URL编码默认使用的字符集是US-ASCII。例如@符号在ASCII字符集中对应的字节为0x40,URL编码之后是%40。对于Unicode字符,使用UTF-8对其进行编码得到相应的字节,然后对每个字节执行百分号编码,例如"汉"使用UTF-8字符集得到的字节为0xE6 0xB1 0x89,经过URL编码之后得到"%E6%B1%89"。
RFC3986文档规定,URL中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符。
保留字符:! * ' ( ) ; : @ & = + $ , / ? # [ ]
不安全字符:一些字符,当他们直接放在URL中的时候,可能会引起解析程序的歧义。这些字符被视为不安全字符。
- 空格:URL在传输的过程,或者用户在排版的过程,或者文本处理程序在处理URL的过程,都有可能引入无关紧要的空格,或者将那些有意义的空格给去掉。
- 引号以及<>:引号和尖括号通常用于在普通文本中起到分隔URL的作用
- #:通常用于表示书签或者锚点
- %:百分号本身用作对不安全字符进行编码时使用的特殊字符,因此本身需要编码
- {}|\^[]`~:某一些网关或者传输代理会篡改这些字符
JavaScript编码实现
Javascript提供了3对URL编码函数:escape/unescape, encodeURI/decodeURI和encodeURIComponent/decodeURIComponent,解码和编码的过程是可逆的。
差异有:
- 安全字符不同:
escape(69个):*/@+-._0-9a-zA-Z
encodeURI(82个):!#$&'()*+,/:;=?@-._~0-9a-zA-Z
encodeURIComponent(71个):!'()*-._~0-9a-zA-Z - Unicode字符的编码方式不同:
对于 Unicode字符,escape的编码方式是%uxxxx(表示unicode字符的4位十六进制字符),encodeURI和encodeURIComponent则使用UTF-8编码,然后再进行百分号编码,这是RFC推荐的。 - 场景不同:
encodeURI被用作对一个完整的URI进行编码,而encodeURIComponent被用作对URI的一个组件进行编 码,所以encodeURIComponent编码的字符范围要比encodeURI的大。
其他
当html的表单被提交时,每个表单都会被URL编码。由于历史的原因,表单使用的URL编码实现并不符合最新的标准。例如空格使用的编码不是%20,而是+号,可能需要自己实现将+号解码成空格的转换。
对于非ASCII字符,使用的编码字符集取决于当前文档使用的字符集。例如head加上<meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> 这样浏览器就会使用gb2312去渲染此文档(无此设置,则浏览器会根据当前用户偏好去自动选择字符集,用户也可以强制当前网页使用某个指定的字符集)。页面文件存储使用的字符集和meta标签中指定的字符集不一致也会导致问题。比如文件实际存储的时候使用的是UTF-8字符集,但是由于meta标签中指定了gb2312,浏览器就会按照gb2312去解析这个文档。
控制台察看结果