关于javaWeb的编码问题

[size=small]用户从浏览器端发起一个 HTTP 请求,需要存在编码的地方是 URL、Cookie、Parameter。服务器端接受到 HTTP 请求后要解析 HTTP 协议,其中 URI、Cookie 和 POST 表单参数需要解码,服务器端可能还需要读取数据库中的数据,本地或网络中其它地方的文本文件,这些数据都可能存在编码问题,当 Servlet 处理完所有请求的数据后,需要将这些数据再编码通过 Socket 发送到用户请求的浏览器里,再经过浏览器解码成为文本。[/size]

[b]一次 HTTP 请求的编码示例[/b]
[img]http://dl.iteye.com/upload/attachment/0062/8958/59b8ffd7-f411-3236-a9d6-3e798f4b1351.bmp[/img]

[b]URL 的几个组成部分[/b]
[img]http://dl.iteye.com/upload/attachment/0062/8956/8b5fd443-b680-39f4-931e-16a49236ba79.gif[/img]

以下测试指定web服务器为Tomcat6.X,浏览器为IE8.X和chrome15.X

[size=small][b]准备:[/b]
1.下面的汉字以“中”为例,分别对应的各种字符集的十六进制编码如下:[/size]
ISO-8859-1: 3f (ISO-8859-1表示的西欧字母,其中并不包含汉字,这里的3f表示不存在的字符)
GB2312: d6d0
GBK: d6d0
UTF-16: feff4e2d
UTF-8: e4b8ad

[size=small]2.关于Http请求头的查看[/size]
三大主流浏览器都有相应的工具
(1).IE下可以使用ieHTTPHeaders
下载地址:http://www.blunck.info/iehttpheaders.html
(2).Firefox可以使用livehttpheaders
(3).chrome本身就自带开发人员工具,可以很方便的查看

[size=small]3. 开发人员必须清楚的servlet规范:[/size]
(1) HttpServletRequest.setCharacterEncoding()方法 仅仅只适用于设置post提交的request body的编码而不是设置get方法提交的queryString的编码。该方法告诉应用服务器应该采用什么编码解析post传过来的内容。
(2) HttpServletRequest.getPathInfo()返回的结果是由Servlet服务器解码(decode)过的。
(3) HttpServletRequest.getRequestURI()返回的字符串没有被Servlet服务器decoded过。
(4) POST提交的数据是作为request body的一部分。
(5) 网页的Http头中ContentType("text/html; charset=GBK")的作用:
(a) 告诉浏览器网页中数据是什么编码;
(b) 表单提交时,通常浏览器会根据ContentType指定的charset对表单中的数据编码,然后发送给服务器的。

这里需要注意的是:这里所说的ContentType是指http头的ContentType,而不是在网页中meta中的ContentType。

[b]1.在浏览器地址栏里输入:[/b]
http://localhost:8080/FileUpload/servlet/FileDownLoadServlet中?filename=中.jpg
在path info和Query String中都指定了汉字

IE8.x Http请求消息
[img]http://dl.iteye.com/upload/attachment/0062/8960/4cce02fb-6d2d-3ee2-bd74-701b42724cf0.png[/img]

Chrome15.x Http请求消息
[img]http://dl.iteye.com/upload/attachment/0062/8962/512d1414-0a97-306f-a41b-fd0726f1f9fd.png[/img]

我们发现path info中都采用的是UTF-8编码,而Query String确采用了不同的编码方式:IE8.x采用的是GBK编码(这边没有正确的显示,至于是什么原因还不清楚),Chrome15.x采用的是UTF-8编码。

浏览器报:
[img]http://dl.iteye.com/upload/attachment/0062/8964/9e303909-c20f-3368-8b35-0994a12421e3.bmp[/img]

根据上面的错误我们可以很明显的知道,Tomcat服务器没有正确的进行解码,从而找不到Servlet。

[b]
了解Tomcat的URL解码:[/b]
1.URL 的 URI 部分进行解码的字符集是在 connector 的 <Connector URIEncoding=”UTF-8”/> 中定义的,如果没有定义,那么将以默认编码 ISO-8859-1 解析。所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码。
2.GET 方式 HTTP 请求的 QueryString 与 POST 方式 HTTP 请求的表单参数都是作为 Parameters 保存,都是通过 request.getParameter 获取参数值。对它们的解码是在 request.getParameter 方法第一次被调用时进行的。request.getParameter 方法被调用时将会调用 org.apache.catalina.connector.Request 的 parseParameters 方法。这个方法将会对 GET 和 POST 方式传递的参数进行解码。

结果:
我们在Servlet的doGet方法中System.out.println(request.getParameter("filename"))
通过chrome访问的Servlet可以正确的显示汉字,而通过IE访问显示乱码
所有我们这里可以知道Tomcat对QueryString采取的解码方式是UTF-8,下面我们要确认的是Tomcat默认对QueryString采取的编码方式是UTF-8还是因为我们设置的URIEncoding=”UTF-8”?

通过这个实验来说明:首先删除server.xml中指定的URIEncoding=”UTF-8”
在浏览器中输入:
http://localhost:8080/FileUpload/servlet/FileDownLoadServlet?filename=中.jpg
结果:
在两个浏览器中都显示乱码,我们在增加两条输出语句:
new String(request.getParameter("filename").getBytes("ISO-8859-1"), "UTF-8");
new String(request.getParameter("filename").getBytes("ISO-8859-1"), "GBK");

IE在第二条输出语句中显示正确的汉字
Chrome在第一条输出语句总显示正确的汉字
结论:
1.在IE中QueryString部分采用的默认编码方式是GBK
2.Tomcat对QueryString的默认解码方式是ISO-8859-1
3.URIEncoding参数提供了对path info和Query String 部分的解码方式

备注:
new String(request.getParameter("filename").getBytes("ISO-8859-1"), "UTF-8")语句可以获取正确的汉字,原因?
[img]http://dl.iteye.com/upload/attachment/0062/8966/95701d2f-e3b7-3c17-8679-879303d6f96d.gif[/img]

ISO-8859-1 字符集的编码范围是 0000-00FF,正好和一个字节的编码范围相对应。这种特性保证了使用 ISO-8859-1 进行编码和解码可以保持编码数值“不变”。虽然中文字符在经过网络传输时,被错误地“拆”成了两个欧洲字符,但由于输出时也是用 ISO-8859-1,结果被“拆”开的中文字的两半又被合并在一起,从而又刚好组成了一个正确的汉字。虽然最终能取得正确的汉字,但是还是不建议用这种不正常的方式取得参数值,因为这中间增加了一次额外的编码与解码。


[b]
2.在已打开的网页上,直接用Get或Post方法发出HTTP请求[/b]
以html页面为例
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
作用1:告诉浏览器网页中数据是什么编码;
2: 表单提交时,通常浏览器会根据ContentType指定的charset对表单中的数据编码,然后发送给服务器的。

IE8.X http请求
[img]http://dl.iteye.com/upload/attachment/0062/8968/13023d73-14a3-38b6-a7fd-4e12d618e467.png[/img]

Chrome15.x Http请求
[img]http://dl.iteye.com/upload/attachment/0062/8970/36426ec0-2cd4-355a-b3f7-666c714ce200.png[/img]


可以看到QueryString部分都采用了charset设置的UTF-8字符集进行编码
我们可以看看主流搜索引擎百度和Google
[img]http://dl.iteye.com/upload/attachment/0062/8972/d2e47fdf-1c28-3950-9025-ff1577fec127.bmp[/img]
可以查看其html源码指定了charset为gb2312
<meta http-equiv="Content-Type" content="text/html;charset=gb2312">
[img]http://dl.iteye.com/upload/attachment/0062/8974/ec321977-3811-3b1b-a3fb-c88bf23481ad.bmp[/img]
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
可以知道,百度默认使用的编码方式是gb2312,而Google默认是UTF-8

在jsp页面中还可以通过其他方式指定编码,和html页面charset效果一样的设置方式还有
contentType,response.setCharacterEncoding(),response.setContentType(),response.setHeader();
response.setContentType(),response.setHeader();优先级最好,其次是response.setCharacterEncoding();再者是<%@page contentType="text/html; chareset=gbk"%>,最后是<meta http-equiv="content-type"content="text/html; charset=gb2312" />


我们经常设置一个Filter,(此时我们设置前台页面的编码为UTF-8),
(1.并加上如下代码:
request.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);

这样我们在Servlet输出参数:
1.System.out.println(request.getParameter("aa");
2.new String(request.getParameter("aa").getBytes("ISO-8859-1"), "UTF-8");
3.new String(request.getParameter("aa").getBytes("ISO-8859-1"), "GBK");

结果:
1正确显示中文,2、3显示乱码,原因:request.setCharacterEncoding对前台的数据进行了解码,所有我们在Servlet中获取参数时不需要再进行解码。
(2.加上如下代码:
request.getParameter("aa");
request.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);

此时的结果是:
2正确显示中文,1、3显示乱码。
原因:前面已经提到” 解码是在 request.getParameter 方法第一次被调用时进行的”,并且此时我们没有在Tomcat中设置URIEncoding属性,默认的解码方式是ISO-8859-1.所以,在request.getParameter("aa");的时候是对UTF-8进行编码的中文用ISO-8859-1进行解码,所有是乱码,此时request.setCharacterEncoding("UTF-8")又对通过ISO-8859-1解码的字符用UTF-8进行解码,所有1结果是乱码。而2刚好可以正确的显示中文,其原因已经在上面解释过了。

总结:出现乱码问题主要是因为编码与解码的字符集不统一造成的,所以对于编码我们自己完全可以指定的我们应该尽量去指定,不要使用默认值,对于浏览器默认设置的我们完全可以动手查看它使用的默认字符集。
UTF-8 在编码效率上和编码安全性上做了平衡,是理想的中文编码方式


补充1:
我们发现不同的浏览器对QueryString采用了不同的编码方式,这就照成了我们服务器端需要对不同的情况进行讨论,其实我们这里可以采取另外一种方式,保证客户端只用一种编码方法向服务器发出请求。
使用Javascript先对URL编码,然后再向服务器提交,不要给浏览器插手的机会。因为Javascript的输出总是一致的,所以就保证了服务器得到的数据是格式统一的

Javascript语言用于编码的函数,一共有三个:
1.escape()虽然这个函数现在已经不提倡使用了,但是由于历史原因,很多地方还在使用它
escape()不能直接用于URL编码,它的真正作用是返回一个字符的Unicode编码值,对应的解码函数是unescape()
2. encodeURI()是Javascript中真正用来对URL编码的函数。
它着眼于对整个URL进行编码,因此除了常见的符号以外,对其他一些在网址中有特殊含义的符号“; / ? : @ & = + $ , #”,也不进行编码。编码后,它输出符号的utf-8形式,并且在每个字节前加上%它对应的解码函数是decodeURI()
3. encodeURIComponent()。与encodeURI()的区别是,它用于对URL的组成部分进行个别编码,而不用于对整个URL进行编码。因此,“; / ? : @ & = + $ , #”,这些在encodeURI()中不被编码的符号,在encodeURIComponent()中统统会被编码。至于具体的编码方法,两者是一样。

对应的在服务器端我们可以通过:
分别是:
URLDecoder.decode(encodeStr,"UTF-8")
URLEncoder.encode(rawStr, "GBK")


参考:[url]http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值