引言:
在进行Web开始时,乱码是我们最经常遇到也是最基本的问题,有经验的程序猿很容易能解决,初学者则容易被泥潭困住。而且很多时候,我们即使解决了乱码问题也是不明就里,往往云里雾里。
其实乱码问题很简单,就是客户端和服务器使用了不一样的字符集导致的。也就是我们发送文件是用的字符编码和解析文件的编码不一致。所以只要搞清楚了我们的文件是怎么被编码和解码的解决乱码就很简单了。分析乱码,我们从请求乱码和响应乱码来分析,请求乱码又需要根据GET和POST来单独分析。
请求乱码——GET
请求的编码是由浏览器发出的,使用GET方法请求服务器信息时,根据HTTP协议规定,Request包是没有请求体的(也就是Request
Body不存在)。所以我们只能把请求参数放在URL中。因此使用GET方式与服务器通信,编码方面我们关心的重点是浏览器对URL的编码方式,和服务器对URL的解码过程。
关于URL
URL是我们经常接触并非常简单的一种技术,URL技术简单到它其实就是一个字符串。实际上URL的结构是很复杂的,只不过通常上用法比较简单而已。关于URL的详细介绍可以参考下面的文章:
URL的规范并定义在RFC 1738文档中。通过URL我们获得通信协议、主机域名、处理端口、应用路径、路径参数、查询参数、页面片段等信息。比如:
http://user:[email protected]/a/b;q=1/c?d=2;sessionid=qewfewrwer#2
根据上面的URL,我们可以得到如下信息:
Part |
Data |
服务器API |
Scheme |
http |
用req. getScheme |
user |
user |
囧,不知道 |
pass |
pass |
囧,不知道 |
host address |
example.com |
req.getServerName |
port |
80 |
req.getServerPort |
path |
/a/b;q=1/c |
req.getContextPath |
query parameters |
d=2;sessionid=qwefewrwer |
req.getQueryString |
fragement |
2 |
开发时,我们经常用到的就是path和query parameters,这两个参数,剩下的参数使用的比较少,不过在RESTful编码中,像路径参数可能会用到。
浏览器对Path部分的编码
path信息被用来匹配处理路径,一般设计上很少在path中包括中文参数。RFC文档对于path的编码也没有明确规定。但是据其他文章的介绍,浏览器对Path的编码一般都会采用UTF-8编码,最新的URI标准已经定义了URI的编码采用UTF-8编码。
定义:简单说path部分就是应用路径部分,就是URL去掉协议、域名、端口和查询信息剩下的部分。
服务器对Path部分的解码:(三种方案)
通常上,我们的请求都会首先发给Web容器(下面以Tomcat为例),URL也会被Web容器解码,对于Tomcat容器来说,我们可以在conf/server.xml的connector标签中增加URL解码参数,默认容器对URL的使用ISO-8859-1解码。
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
上面的是Tomcat的默认设定,可以给标签添加URIEncoding属性来指定URL的解码方案。(PS:标签写法是URI不是URL)
如果不想使用这种硬解码方案,还可以指定另一个属性:useBodyEncodingForURI,这个属性用来告诉Web容器,如果request指定了解码方案,则使用request.setCharacterEncoding指定的编码来解码URL。
第二种方案没有经过测试,如果有需要可以尝试下。详细资料可以参考下面的Tomcat官方文档:
http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q2
此外,如果不想修改容器的全局配置,毕竟有时候容器里可能不止我们一个应用,那么我们还可以采用下面的做法来提取参数:
String path = req.getServerPath();//自己手动提取,不适合配合框架 path = new String(path.getBytes(“ISO8859-1”,”UTF-8”));//重新拼装
上面的做法,我们要确定Web容器对URL的解码用的是ISO8859-1,因为不排除其他人修改了容器配置或容器配置本身比较奇葩的可能。
浏览器对QueryParameter的编码
查询参数和Path是不一样的,缺少查询参数,web容器是可以定位到我们的处理程序的,但是缺少path就不行。另外,path和查询参数的保留字符是不一样的。
定义:简单来说查询参数就是path后面紧跟的?后面的部分,用&来连接各个查询参数。
由于Path和查询参数的不同,有些浏览器对查询参数的编码和path部分的编码是不一致的。具体使用怎么编码的比较混乱,可以参考下下面的文章:
根据上面的文章总结的规律:
(1)Path部分或者说除查询参数外的URL部分,各浏览器用UTF-8编码;
(2)查询参数,各浏览器根据操作系统编码决定;
上面的文章比较老了,规律可能不实用了,但是也能说明一定问题。对于某些文章说的,查询参数会根据页面编码来决定,我没有做实验,但是这种结论肯定是片面的。原因如下:
页面的meta参数是用来向浏览器说明页面编码的,其次,在使用POST Method发送数据的时候,浏览器会根据meta的编码来编码Request Body。而Get方式,我们在没有页面的时候也可以发起,所以浏览器根本找不到Meta标签,也就没法参考页面编码。
浏览器对查询参数到底使用哪种方式编码的,我没有找到专业、权威、可信的答案,但是我认为这个还是具体情况具体分析,做个小实验就行了。毕竟时代在进步,厂商们统一使用UTF-8编码的可能性比较大。而且后面有不依赖浏览器编码的解决方案。
服务器对QueryParameter的解码
查询参数也是URL的一部分,所以Web容器对查询参数的解码比较明智,解码和path使用的是一样的方案的编码,所以解决方案也是一样的。
出现乱码:
在处理查询参数时,我们常用req.getParameters();来获取某个参数,这个方法背后很少有人关心它的工作原理,而且也没必要。这一部分是最容易出现乱码的,毕竟它里面的参数可能是用户输入的,并不是我们设计的。在GET方式下,出现这种乱码不要慌张,首先我们要分析出,浏览器对查询参数到底采用了哪种编码。方法简单(也复杂),chrome下F12打开开发者工具
找到network标签,可以看到Request URL中显示的是k= %E4%B8%AD%E5%9B%BD,把%去掉,可以得到6个16进制数,百度下unicode码表,可以看到他们正好是“中”和“国”的unicode编码。所以可以猜测浏览器使用的是UTF-8编码。这种判断方式需要对字符编码比较熟悉。不过也不算很难,找点字符编码的文章学学很容易就能看出规律来。
PS:不要通过浏览器的地址栏看URL编码,很多浏览器的地址栏会对URL解码显示。
之后,服务器端,首先确定下,你的Web容器对URL使用的解码方案,然后相应的选择String(param.getBytes(“ISO8895-1”,”UTF-8”))或者是useBodyEncodingForURI、URIEncoding方案就好了。
总结:
使用GET方式出现乱码时,最主要的是找出浏览器对URL的编码方式,如果使用JS编程时,在浏览器可以使用encodeURIComponent函数对中文参数进行编码后再拼装参数。Java端使用URLDecoder.decode方法解码。JS端要进行两次编码,否则第一次的URL编码会被Web容器解码,获取的参数仍有可能是乱码。可以参考:
版权声明:本文为博主原创文章,未经博主允许不得转载。