Fork me on GitHub
代码改变世界

JS的URL编码

2015-11-15 15:30  autrol  阅读(313)  评论(0编辑  收藏  举报

背景

URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号,这是网络标准:

只有字母和数字[0-9a-zA-Z]、一些特殊符号"$-_.+!*'(),"[不包括双引号]、以及某些保留字(;/?:@&=),才可以不经过编码直接用于URL。

这意味着,如果URL中有特殊字符,就必须编码后使用。但是麻烦的是,标准中未规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致"URL编码"成为了一个混乱的领域。

接下来会依次分析三种不同的情况,在每种情况中,浏览器的URL编码方法都不一样。把它们的差异解释清楚之后,我再说如何用Javascript找到一个统一的编码方法。

浏览器的URL编码方法

一、网络路径中包含特殊字符

打开Chrome浏览器,输入"http://www.cnblogs.com/yangzhinian/技术",最终在控制台中请求的URL为:http://www.cnblogs.com/yangzhinian/%E6%8A%80%E6%9C%AF

Firefox的也是同样的结果,最终我们来验证使用的是什么编码方式

decodeURIComponent("http://www.cnblogs.com/yangzhinian/%E6%8A%80%E6%9C%AF");
//结果为:http://www.cnblogs.com/yangzhinian/技术

结论1:网址路径的编码,用的是utf-8编码。

 

二、查询字符串包含特殊字符

在window 7操作系统下,在Chrome、Firefox浏览器中,输入"http://www.cnblogs.com/yangzhinian?wd=春节",最终在控制台中请求的URL为:

http://www.cnblogs.com/yangzhinian/?wd=%E6%98%A5%E8%8A%82

但是在window XP操作系统中,Chrome、Firefox浏览器url为:

http://www.cnblogs.com/yangzhinian/?wd=%B4%BA%BD%DA

结论2:查询字符串的编码,用的是操作系统的默认编码。

 

三、Get、POST方法生成的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编码。因此,从它们的搜索框中搜索同一个词"春节",生成的查询字符串是不一样的。

结论3:GET和POST方法的编码,用的是网页的编码。

 

四、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"。

结论4:在Ajax调用中,IE总是采用GB2312编码(操作系统的默认编码),而Firefox、Chrome总是采用utf-8编码。

Javascript编码解码方法

假定前面你都看懂了,那么此时你应该会感到很头痛。因为实在太混乱了。不同的操作系统、不同的浏览器、不同的网页字符集,将导致完全不同的编码结果。如果程序员要把每一种结果都考虑进去,是不是太恐怖了?有没有办法,能够保证客户端只用一种编码方法向服务器发出请求?

回答是有的,就是使用Javascript先对URL编码,然后再向服务器提交,不要给浏览器插手的机会。因为Javascript的输出总是一致的,所以就保证了服务器得到的数据是格式统一的。

Javascript语言用于编码的函数,一共有三个:escape/encodeURI/encodeURIComponent。最古老的一个就是escape(),虽然这个函数现在已经不提倡使用了,但是由于历史原因,很多地方还在使用它。

一、escape/unescape

escape:该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码:*@-_+./ 。其他所有的字符都会被转义序列替换。

unescape:是escape对应的解码方法。

实际上,escape()不能直接用于URL编码,它的真正作用是返回一个字符的Unicode编码值

如:"春节"的返回结果是%u6625%u8282,也就是说在Unicode字符集中,"春"是第6625个(十六进制)字符,"节"是第8282个(十六进制)字符。

escape("春节");            //结果:%u6625%u8282

unescape("%u6625%u8282"); //结果:春节

注意:escape()不对"+"编码。但是我们知道,网页在提交表单的时候,如果有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处理成空格。所以,使用的时候要小心

 

二、encodeURI/decodeURI

encodeURI:该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码:-_.!~*'() 。编码后,它输出符号的utf-8形式,并且在每个字节前加上%。

该方法的目的是对 URI 进行完整的编码,因此对以下在 URI 中具有特殊含义的 ASCII 标点符号,encodeURI() 方法是不会进行转义的:;/?:@&=+$,#

decodeURI:是encodeURI对应的解码方法。如:

encodeURI("春节");//结果:%E6%98%A5%E8%8A%82

decodeURI("%E6%98%A5%E8%8A%82");//结果:春节

 

三、encodeURIComponent/decodeURIComponent

encodeURIComponent:该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码:-_.!~*'() 。其他字符(比如 :;/?:@&=+$,# 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。编码后,它输出符号的utf-8形式,并且在每个字节前加上%。

decodeURIComponent:是encodeURIComponent对应的解码方法。如:

encodeURIComponent('春节');//结果:%E6%98%A5%E8%8A%82

decodeURIComponent("%E6%98%A5%E8%8A%82");//结果:"春节"