跳到主要内容

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。例如aUS-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 / decodeURIencodeURIComponent / decodeURIComponent。其中,escapeencodeURIencodeURIComponent——都是用于将不安全不合法的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() 是从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编码,而不依赖于浏览器的默认实现。

参考资料

HTTP URL 字符转义 字符编码 、 RFC 3986编码规范

为什么要进行URL编码