一、问题的由来
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()
。