jsp/servlet中的编码问题
首先声明以下只是我个人的看法,有部分观点与网上人云亦云的观点不一样,不过凡事必恭亲,我还是相信自己测试的结果
推荐一个很好地URL编码详解http://www.ruanyifeng.com/blog/2010/02/url_encoding.html
与网上的共识是
JSP中
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
二者里面的contentType="text/html; charset=UTF-8"作用是一样的,只不过后者优先级高于前者
相当于resposne.setContentType("text/html;charset=utf-8")作用是指定HTTP响应的编码,同时指定了浏览器显示的编码
即服务器将页面以utf-8传输到浏览器,浏览器再以utf-8显示
pageEncoding="UTF-8" 指定的jsp页面本身的编码为utf-8,同时在被服务器转换为.java文件时采用utf-8编码(默认使用iso-8859-1),
但是.java文件编译为.class文件统一使用utf-8编码(JVM规定如此)
pageEncoding指定的编码一定要与JSP保存的编码一致
可以在window-preference-web-jsp Files修改jsp默认保存编码(已保存的JSP编码无法修改,只能右击修改)
最好将contentType和pageEncoding指定的编码一致
以下关于JSP页面的不同提交方式使用的编码是我与网上一些观点的不同
向服务器提交数据的方式有get和post
以下是测试
<body> <form action="/webExer/LoginServlet" method="post"> 登录:<input type="text"><br> 验证码:<input type="text" name="imageCode"> <!-- <img src="/webExer/ImageCodeServlet"><br> --> <input type="submit" value="提交"> </form> <input type="button" value="get跳转" onclick="jump()"> <script type="text/javascript"> function jump() { window.location.href = "/webExer/LoginServlet?imageCode=get提交"; } </script> </body>
public class LoginServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //request.setCharacterEncoding("UTF-8"); //response.setContentType("text/html;charset=utf-8"); String imageCode=request.getParameter("imageCode"); String x = new String(imageCode.getBytes("ISO-8859-1"),"UTF-8");
String xx=new String(imageCode.getBytes("ISO-8859-1"),"GBK");
//原生数据 System.out.println(imageCode);
//转码后数据 System.out.println(x);
System.out.println(xx); PrintWriter out=response.getWriter(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
当访问JSP时
POST方式
点击post提交按钮时form表单以post向后台传数据,并在控制台打印获得的数据
指定为UTF-8编码
<%@ page language="java" contentType="text/html; charset=UTF-8"
后台中可以发现原生数据是乱码,但是IS0-8859-1转UTF-8后数据正常显示发现post提交过来的是ISO-8859-1编码
那为什么IS0-8859-1转GBK是乱码呢?因为JSP页面是UTF-8,浏览器用的UTF-8编码显示JSP页面,
post提交时默认采用当前浏览器使用的编码即UTF-8提交,但是服务器有自己的编码,在POST提交方式下,注意我说的是在POST提交方式下,跟GET无关,tomcat默认的编码是ISO-8859-1(在server.xml里修改对该默认编码无效,下面会进行测试),于是tomcat又用IS0-8859-1重新编码了一次,也就是传到servlet里的数据经过了两次编码,先UTF-8,在ISO-8859-1,所以拿到IS0-8859-1解码的字节后转GBK当然会乱码了。(网上大都说get方式是先采用浏览器面默认编码,在由服务器用IS08859-1进行二次编码,但我测试的是post方式这样做的,get方式不是,get方式会在下面介绍,强烈建议自己动手试试)
解决办法一般用request.setCharacterEncoding("UTF-8");但是该方式只能用于post方式
request.setCharacterEncoding("UTF-8");
这是在控制台显示,如果要在浏览器中正常显示别忘了加上response.setContentType("text/html;charset=utf-8");
为了证明自己的测试结果,我把contentType设置为"text/html; charset=GBK"试试
contentType="text/html; charset=GBK"
String imageCode=request.getParameter("imageCode");
String y=new String(imageCode.getBytes("UTF-8"));
String yy=new String(imageCode.getBytes("GBK"));
String x = new String(imageCode.getBytes("ISO-8859-1"),"UTF-8");
String xx=new String(imageCode.getBytes("ISO-8859-1"),"GBK");
System.out.println(imageCode);
System.out.println(y);
System.out.println(yy);
System.out.println(x);
System.out.println(xx);
可以看到只有 String xx=new String(imageCode.getBytes("ISO-8859-1"),"GBK");没有乱码,证明我的想法是对的
再证明修改server.xml对POST方式默认编码无效
在servler.xml中指定任意编码
结果
与前面GBK编码测试结果一样,说明server.xml指定的编码对POST方式无效,再次证明了我的想法
GET方式
先测试UTF-8(此时server.xml里未指定编码)
GET方式就是在URL后面拼接参数,我在JS里传了一个死参数
<input type="button" value="get跳转" onclick="jump()"> <script type="text/javascript"> function jump() { window.location.href = "/webExer/LoginServlet?imageCode=get提交"; } </script>
servlet中
String imageCode=request.getParameter("imageCode");
String y=new String(imageCode.getBytes("UTF-8"));
String yy=new String(imageCode.getBytes("GBK"));
String x = new String(imageCode.getBytes("ISO-8859-1"),"UTF-8");
String xx=new String(imageCode.getBytes("ISO-8859-1"),"GBK");
System.out.println(imageCode);
System.out.println(y);
System.out.println(yy);
System.out.println(x);
System.out.println(xx);
控制台打印
可以看到jsp传过来的参数直接获取就能正常显示
即String y=new String(imageCode.getBytes("UTF-8"))能正常显示,即传过来的数据是UTF-8编码
也就是说get方式传到后台的数据是UTF-8,那么这个UTF-8编码是不是浏览器当前默认编码呢?
我用GBK试了一下
contentType="text/html; charset=GBK
很奇怪结果全是乱码,但是浏览器默认编码确实变了
注意到一个细节
使用contentType="text/html; charset=UTF-8”时浏览器URL参数显示为中文
使用contentType="text/html; charset=GBK”时浏览器URL参数显示其他的编码
不知道用的什么编码,但是知道春"和"节"的GBK编码分别是"B4 BA"和"BD DA"。于是试了试把“get提交”改成“get春节”
说明浏览器确实用的GBK将GET参数编码了,也就是说确实使用浏览器当前默认编码对GET参数编码
既然客户端是GBK编码,如果与POST方式一样tomcat用ISO-8859-1二次编码,那为什么在后台
String xx=new String(imageCode.getBytes("ISO-8859-1"),"GBK");用ISO-8859-1转GBK获取还是乱码呢?
说明我的tomcat获取GET参数不是用的ISO-8859-1二次编码,又因为GBK用UTF-8转码是转不过去的
原因GBK用UTF-8不能转码参照http://my.oschina.net/chape/blog/201725
而前面的用UTF-8测试的例子只有
String imageCode=request.getParameter("imageCode")和String y=new String(imageCode.getBytes("UTF-8"))
能取到正常值,所以我猜测tomcat直接用了UTF-8解码,也就导致了用过客户端用GBK后台用UTF-8转不了码,只能乱码了
于是我又做了个测试
在tomcat的server.xml里设置服务器编码URIEncoding="ISO-8859-1"
如果server.xml设置编码对GET方式有效的话,tomcat肯定是用ISO-8859-1二次编码了
在原来的GBK中再次测试
发现只有String xx=new String(imageCode.getBytes("ISO-8859-1"),"GBK");取到正常值
即修改server.xml编码对GET方式是有效的
其过程应该是客户端用GBK编码,服务器端用ISO-8859-1编码,我在servlet里手动用ISO-8859-1转码
这样看来我的tomcat在GET方式下默认编码应该是UTF-8,而不是网上很多人说的ISO-8859-1,
至于为什么是UTF-8我也不清楚,我用的tomcat8.0,JDK8,项目保存为GBK(UTF-8也试过)
!后来重新试验过,客户端使用的UTF-8,server.xml里指定ISO-8859-1二次编码,
在servlet里直接new String(imageCode.getBytes("ISO-8859-1")也能获取到正常值,String
里获取的ISO-8859-1编码后用的本地默认编码进行解码,本地默认编码就是你的.java文件保存的编码(在eclipse指的是workspace指定的编码),我保存为UTF-8,所以能获取到正常值,如果改为GBK则会乱码。
所以猜测servlet里解码采用的编码是你的平台默认编码!
隔了一段时间后我换了tomcat7,发现在server.xml默认没有编码的情况下,get和post提交在servlet里都是经过ISO-8859-1编码,与前面的结果不太一样,看来是tomcat版本不太,服务器采用的编码方式有所差异,
这样的话最好在tomcat的server.xml里指定get方式的二次编码为ISO-8859-1,在用过滤器统一编码的时候也方便处理
这样要想GET方式提交的参数不乱码最好的方式是在客户端使用UTF-8编码,如果一定要用其他编码,
最好用JS将参数编码为UTF-8再提交到后台
有一点需要知道:POST下的request.setCharacterEncoding("UTF-8")对GET方式无效
至于servlet中的响应编码使用response.setContentType("text/html;charset=utf-8")就可以了,该方法
指定HTTP的响应编码为UTF-8并通知浏览器以UTF-8编码显示。
总结:
1丶 不管get还是post方式提交的参数都是采用浏览器当前编码
2丶 针对tomcat7及以下版本,参数传入servlet后,两种方式默认情况下在servlet里采用ISO-8859-1解码,
针对中文,比如浏览器采用GBK,servlet可以使用new String(value.getBytes("ISO-8859-1"),"GBK")获取正确参数
针对tomcat8版本,get方式提交的参数传入servlet后采用的平台默认编码解码
3丶 request.setCharacterEncoding()只对post方式有效,针对get方式,可以二次转码获取中文,或者在tomcat下的server.xml里指定编码
4丶 response.setCharacterEncoding()设置HTTP相应编码
response.setContentType()设置HTTP相应编码并通知浏览器解码方式