一、问题的由来
URL 就是网址,只有上网,就肯定会用到。
一般来说,URL 只能应用英文字母、阿拉伯数字和某些标点符号,不能应用其余文字和符号。比方,世界上有英文字母的网址 ”http://www.abc.com”,然而没有希腊字母的网址 ”http://www.aβγ.com”(读作阿尔法 - 贝塔 - 伽玛.com)。这是因为网络规范 RFC 1738 做了硬性规定:
“…Only alphanumerics [0-9a-zA-Z], the special characters “$-_.+!*'(),” [not including the quotes – ed], and reserved characters used for their reserved purposes may be used unencoded within a URL.”
“ 只有字母和数字[0-9a-zA-Z]、一些特殊符号 ”$-_.+!*'(),”[不包含双引号]、以及某些保留字,才能够不通过编码间接用于 URL。”
这意味着,如果 URL 中有汉字,就必须编码后应用。然而麻烦的是,RFC 1738 没有规定具体的编码方法,而是交给应用程序(浏览器)本人决定。这导致 ”URL 编码 ” 成为了一个凌乱的畛域。
上面就让咱们看看,”URL 编码 ” 到底有多凌乱。我会顺次剖析四种不同的状况,在每一种状况中,浏览器的 URL 编码方法都不一样。把它们的差别解释分明之后,我再说如何用 Javascript 找到一个对立的编码方法。
二、状况 1:网址门路中蕴含汉字
关上 IE(我用的是 8.0 版),输出网址 http://zh.wikipedia.org/wiki/ 春节
。留神, 春节
这两个字此时是网址门路的一部分。
查看 HTTP 申请的头信息,会发现 IE 理论查问的网址是http://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82
。也就是说,IE 主动将 ” 春节 ” 编码成了%E6%98%A5%E8%8A%82
。
咱们晓得,” 春 ” 和 ” 节 ” 的 utf- 8 编码别离是 E6 98 A5
和E8 8A 82
,因而,%E6%98%A5%E8%8A%82
就是依照程序,在每个字节前加上 %
而失去的。(具体的转码办法,请参考我写的《字符编码笔记》。)
在 Firefox 中测试,也失去了同样的后果。所以,论断 1 就是,网址门路的编码,用的是 utf- 8 编码。
三、状况 2:查问字符串蕴含汉字
在 IE 中输出网址http://www.baidu.com/s?wd= 春节
。留神,” 春节 ” 这两个字此时属于查问字符串,不属于网址门路,不要与状况 1 混同。
查看 HTTP 申请的头信息,会发现 IE 将 春节
转化成了一个乱码。
切换到十六进制形式,能力分明地看到,” 春节 ” 被转成了B4 BA BD DA
。
咱们晓得,春
和节
的 GB2312 编码(我的操作系统 ”Windows XP” 中文版的默认编码)别离是 B4 BA
和BD DA
。因而,IE 实际上就是将查问字符串,以 GB2312 编码的格局发送进来。
Firefox 的解决办法,略有不同。它发送的 HTTP Head 是wd=%B4%BA%BD%DA
。也就是说,同样采纳 GB2312 编码,然而在每个字节前加上了%
。
所以,论断 2 就是,查问字符串的编码,用的是操作系统的默认编码。
四、状况 3:Get 办法生成的 URL 蕴含汉字
后面说的是间接输出网址的状况,然而更常见的状况是,在已关上的网页上,间接用 Get 或 Post 办法收回 HTTP 申请。
依据台湾中兴大学吕瑞麟老师的试验,这时的编码方法由网页的编码决定,也就是由 HTML 源码中字符集的设定决定。
<meta http-equiv="Content-Type" content="text/html;charset=xxxx">
如果下面这一行最初的 charset 是 UTF-8,则 URL 就以 UTF- 8 编码;如果是 GB2312,URL 就以 GB2312 编码。
举例来说,百度是 GB2312 编码,Google 是 UTF- 8 编码。因而,从它们的搜寻框中搜寻同一个词 ” 春节 ”,生成的查问字符串是不一样的。
百度生成的是%B4%BA%BD%DA
,这是 GB2312 编码。
Google 生成的是%E6%98%A5%E8%8A%82
,这是 UTF- 8 编码。
所以,论断 3 就是,GET 和 POST 办法的编码,用的是网页的编码。
五、状况 4:Ajax 调用的 URL 蕴含汉字
后面三种状况都是由浏览器收回 HTTP 申请,最初一种状况则是由 Javascript 生成 HTTP 申请,也就是 Ajax 调用。还是依据吕瑞麟老师的文章,在这种状况下,IE 和 Firefox 的解决形式齐全不一样。
举例来说,有这样两行代码:
url = url + "?q=" +document.myform.elements[0].value; // 假设用户在表单中提交的值是 "春节" 这两个字
http_request.open('GET', url, true);
那么,无论网页应用什么字符集,IE 传送给服务器的总是q=%B4%BA%BD%DA
,而 Firefox 传送给服务器的总是q=%E6%98%A5%E8%8A%82
。也就是说,在 Ajax 调用中,IE 总是采纳 GB2312 编码(操作系统的默认编码),而 Firefox 总是采纳 utf- 8 编码。这就是咱们的论断 4。
六、Javascript 函数:escape()
好了,到此为止,四种状况都说完了。
假设后面你都看懂了,那么此时你应该会感到很头痛。因为,切实太凌乱了。不同的操作系统、不同的浏览器、不同的网页字符集,将导致齐全不同的编码后果。如果程序员要把每一种后果都思考进去,是不是太恐怖了?有没有方法,可能保障客户端只用一种编码方法向服务器发出请求?
答复是有的,就是应用 Javascript 先对 URL 编码,而后再向服务器提交,不要给浏览器插手的机会。因为 Javascript 的输入总是统一的,所以就保障了服务器失去的数据是格局对立的。
Javascript 语言用于编码的函数,一共有三个,最古老的一个就是escape()
。尽管这个函数当初曾经不提倡应用了,然而因为历史起因,很多中央还在应用它,所以有必要先从它讲起。
实际上,escape()
不能间接用于 URL 编码,它的真正作用是返回一个字符的 Unicode 编码值。比方 ” 春节 ” 的返回后果是 %u6625%u8282
,也就是说在 Unicode 字符集中, 春
是第 6625 个(十六进制)字符,节
是第 8282 个(十六进制)字符。
它的具体规定是,除了 ASCII 字母、数字、标点符号 @ * _ + - . /
以外,对其余所有字符进行编码。在 \u0000
到\u00ff
之间的符号被转成 %xx
的模式,其余符号被转成 %uxxxx
的模式。对应的解码函数是unescape()
。
所以,Hello World
的 escape()
编码就是Hello%20World
。因为空格的 Unicode 值是20
(十六进制)。
还有两个中央须要留神。
首先,无论网页的原始编码是什么,一旦被 Javascript 编码,就都变为 unicode 字符。也就是说,Javascipt 函数的输出和输入,默认都是 Unicode 字符。这一点对上面两个函数也实用。
其次,escape()
不对 +
编码。然而咱们晓得,网页在提交表单的时候,如果有空格,则会被转化为 +
字符。服务器解决数据的时候,会把 +
号解决成空格。所以,应用的时候要小心。
七、Javascript 函数:encodeURI()
encodeURI()
是 Javascript 中真正用来对 URL 编码的函数。
它着眼于对整个 URL 进行编码,因而除了常见的符号以外,对其余一些在网址中有非凡含意的符号; / ? : @ & = + $ , #
,也不进行编码。编码后,它输入符号的 utf- 8 模式,并且在每个字节前加上%
。
它对应的解码函数是decodeURI()
。
须要留神的是,它不对单引号 ’ 编码。
八、Javascript 函数:encodeURIComponent()
最初一个 Javascript 编码函数是 encodeURIComponent()
。与encodeURI()
的区别是,它用于对 URL 的组成部分进行个别编码,而不用于对整个 URL 进行编码。
因而,; / ? : @ & = + $ , #
,这些在 encodeURI()
中不被编码的符号,在 encodeURIComponent()
中通通会被编码。至于具体的编码方法,两者是一样。
它对应的解码函数是decodeURIComponent()
。