服务器:Tomcat5.5
浏览器:Firfox3.5、IE7.0
import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; public class HttpServer { public static void main(String[] args) { try { ServerSocket sSocket = new ServerSocket(8088); Socket socket = sSocket.accept(); InputStream is = socket.getInputStream(); //String encode = "GB2312"; String encode = "UTF-8"; putStream(is, encode); } catch (IOException e) { e.printStackTrace(); } } public static void putStream(InputStream is, String encode) throws IOException, UnsupportedEncodingException { byte[] content = new byte[1024]; int readCount = is.read(content); OutputStreamWriter bos = new OutputStreamWriter(System.out); while (readCount != 0) { bos.write(new String(content, 0, readCount, encode)); bos.flush(); readCount = is.read(content); } is.close(); } }
gb2312.jsp代码清单:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <body> <form name=form1 action="" method="post"> <input type="text" name="textParam1" size="50 px" value="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"> <br> <input type="file" name="fileParam" size="50 px" value=""> <br> <input type="button" value="submit" onclick="submitForm()"> </form> <script type="text/javascript"> function submitForm(){ var str ="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"; //form1.action = "gb2312rs.jsp?qryParam1=" + encodeURIComponent(str) + "&qryParam2="+ encodeURIComponent(form1.textParam1.value) ; form1.action = "http://localhost:8088/gb2312rs.jsp?qryParam1=" + encodeURIComponent(str) + "&qryParam2="+ encodeURIComponent(form1.textParam1.value) ; form1.submit(); } </script> </body>
gb2312rs.jsp清单:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <head> <title>Insert title here</title> </head> <body> <% //这里一定要设置,且与浏览的提交时编码方式一样,否则乱码(默认Tomcat以iso8859-1解码) //且只能post提交方式的参数起使用,对URL后附带参数不起作用 request.setCharacterEncoding("GB2312"); String textParam1 = request.getParameter("textParam1"); String qryParam1 = request.getParameter("qryParam1"); System.out.println("textParam1=" + textParam1); System.out.println("qryParam1=" + qryParam1); System.out.println("qryParam1=" + (qryParam1==null?null:new String(qryParam1.getBytes("iso8859-1"),"utf-8"))); %> request.getParameter("textParam1")(<font color=red>GB2312编码->GB2312解码</font>):<br> <%=request.getParameter("textParam1")%> <br><hr> request.getParameter("qryParam1")(<font color=red>UTF-8编码->ISO8859-1解码</font>):<br> <%=qryParam1%> <br><hr> String(request.getParameter("qryParam1").getBytes("iso8859-1"),"UTF-8")(<font color=red>UTF-8编码->ISO8859-1解码->ISO8859-1编码->UTF-8解码</font>):<br> <%=qryParam1==null?null:new String(qryParam1.getBytes("iso8859-1"),"UTF-8")%> </body> </html>
1、以POST方式提交表单
2、请求的URL后还附加参数且参数值含有中文
3、附加参数值经过了encodeURIComponent()函数编码
4、Tomcat未设置URIEncoding与useBodyEncodingForURI
5、请求页面与结果页面<%@ page %>指令的contentType与pageEncoding编码方式都为GB2312
6、结果页面中request的编码方式为GB2312
请求页面gb2312.jsp图:
响应页面gb2312rs.jsp图:
响应页面gb2312rs.jsp编码方式:
响应页面gb2312rs.jsp地址栏显示如下:
http://localhost:8080/HttpStream/gb2312rs.jsp?qryParam1=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%22%20%3C%3E%3F%60-%3D%5B%5D%5C%3B'%2C.%2F&qryParam2=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%5C%22%20%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B'%2C.%2F
POST /gb2312rs.jsp?qryParam1=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%22%20%3C%3E%3F%60-%3D%5B%5D%5C%3B'%2C.%2F&qryParam2=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%5C%22%20%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B'%2C.%2F HTTP/1.1
textParam1=%D6%D0a+%7E%21@%23%24%25%5E%26*%28%29_%2B%7B%7D%7C%3A%5C%22+%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B%27%2C.%2F&fileParam= |
从上面码流分析可知
直接通过 request.getParameter("qryParam1") 提取附加参数时,参数值为乱码。
客户端编码:虽然采用的POST方式请求,但是参数是附加在URL后面的,结果就是附加在URL后面的参数还是以GET方式传递。又encodeURIComponent()函数是以UTF-8编码方式对内容进行编码,经过此函数编码后的附加参数的值就以UTF-8传送到服务器。
服务器解码:因为Tomcat的<Connector/>标签未设置URIEncoding与useBodyEncodingForURI这两个属性,所以Tomact对URL后的附加参数采用默认的编码方式ISO8859-1进行编码。
最后结论:客户端编码方式(UTF-8)与服务器解码方式(ISO8859-1)不同导致了乱码。
1、在结果页面读取附加参数时这样做:new String(request.getParameter("qryParam").getBytes("ISO8859-1"),"UTF-8"),原理很简单,就是因为服务器用ISO8859-1编码方式对URL附加参数进行了解码,所以我们这里再来用ISO8859-1来对解码出的串进行一次编码,这样就可以拿到浏览器传给服务器的原始码流,这样再用浏览器发送时所使用的UTF-8编码方式重新解码即可得到正确的内容。
2、修改Tomcat配置文件server.xml中的<Connector ... URIEncoding="UTF-8"... />也可。原理,URIEncoding设置的编码方式为专门用来解码GET方式(或URL拼接)的参数。
gb2312.jsp代码清单:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <body> <form name=form1 action="" method="post"> <input type="text" name="textParam1" size="50 px" value="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"> <br> <input type="file" name="fileParam" size="50 px" value=""> <br> <input type="button" value="submit" onclick="submitForm()"> </form> <script type="text/javascript"> function submitForm(){ var str ="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"; form1.action = "gb2312rs.jsp?qryParam1=" + (str) + "&qryParam2="+ (form1.textParam1.value) ; //form1.action = "http://localhost:8088/gb2312rs.jsp?qryParam1=" + (str) + "&qryParam2="+ (form1.textParam1.value) ; form1.submit(); } </script> </body> </html>
gb2312rs.jsp清单:运行过程图:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <head> <title>Insert title here</title> </head> <body> <% //这里一定要设置,且与浏览的提交时编码方式一样,否则乱码(默认Tomcat以iso8859-1解码) //且只能post提交方式的参数起使用,对URL后附带参数不起作用 request.setCharacterEncoding("GB2312"); String textParam1 = request.getParameter("textParam1"); String qryParam1 = request.getParameter("qryParam1"); System.out.println("textParam1=" + textParam1); System.out.println("qryParam1=" + qryParam1); System.out.println("qryParam1=" + (qryParam1==null?null:new String(qryParam1.getBytes("iso8859-1"),"utf-8"))); %> request.getParameter("textParam1")(<font color=red>GB2312编码->GB2312解码</font>):<br> <%=request.getParameter("textParam1")%> <br><hr> request.getParameter("qryParam1")(<font color=red>UTF-8编码->ISO8859-1解码</font>):<br> <%=qryParam1%> <br><hr> String(request.getParameter("qryParam1").getBytes("iso8859-1"),"gb2312")(<font color=red>GB2312编码->ISO8859-1解码->ISO8859-1编码->GB2312解码</font>):<br> <%=qryParam1==null?null:new String(qryParam1.getBytes("iso8859-1"),"gb2312")%> </body> </html>
响应页面gb2312rs.jsp图:
提交后地址栏显示如下:
http://localhost:8080/HttpStream/gb2312rs.jsp?qryParam1=中a%20~!@#$%^&*()_+{}|:"%20<>?`-=[]\;',./&qryParam2=中a%20~!@#$%^&*()_+{}|:\"%20<>?`-=[]\\;',./
POST /gb2312rs.jsp?qryParam1=中a%20~!@ HTTP/1.1
textParam1=%D6%D0a+%7E%21@%23%24%25%5E%26*%28%29_%2B%7B%7D%7C%3A%5C%22+%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B%27%2C.%2F&fileParam= |
从HTTP码流可以看出:
1、以POST方式提交表单
2、请求的URL后还附加参数且参数值含有中文
3、附加参数值没有经过encodeURIComponent()函数编码
4、Tomcat未设置URIEncoding与useBodyEncodingForURI
5、请求页面与结果页面<%@ page %>指令的contentType与pageEncoding编码方式都为GB2312
6、结果页面中request的编码方式为GB2312
1、直接以request.getParameter("qryParam1")方式提取附加参数时,参数值为乱码。
2、附加参数qryParam1值从“#”字符开始丢失,问题是因数#在URL为特殊字符,表示书签,所以#字符后面的所有内容没有传送到服务器,使用encodeURIComponent()对参数值进行编码即可解决。
客户端编码:虽然采用的POST方式请求,但是参数是附加在URL后面的,结果就是附加在URL后面的参数还是以GET方式传递。以GET方式传递的参数又未先使用encodeURIComponent()函数对值进行编码,所以使用浏览器的编码方式(这里实质上就是请求页面设计的编码方式GB2312)编码后发送。
服务器解码:因为Tomcat的<Connector/>标签未设置URIEncoding与useBodyEncodingForURI这两个属性,所以Tomact对URL后的附加参数采用默认的编码方式ISO8859-1进行解码。
最后结论:客户端编码方式(GB2312)与服务器解码方式(ISO8859-1)不同导致了乱码。
1、在结果页面读取附加参数时这样做:new String(request.getParameter("qryParam").getBytes("ISO8859-1"),"GB2312")
2、修改Tomcat配置文件server.xml中的<Connector ... URIEncoding="GB2312"... />也可。
3、修改Tomcat配置文件server.xml中的<Connector ... useBodyEncodingForURI="true"... />也可。
原理,Tomcat在解码客户端发以GET方式(或URL附加参数)发过来的码流时,编码方式与请求体的编码方式一致。而请求体的编码方式是由request来设置的,又因服务器的对HTTP请求体采用的编码方式为GB2312,所以最后服务对GET方式传递的内容也采用GB2312方式来解码,这样就不会出现乱码问题了。
gb2312.jsp清单:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <body> <form name=form1 action="" method="get"> <input type="text" name="textParam1" size="50 px" value="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"> <br> <input type="file" name="fileParam" size="50 px" value=""> <br> <input type="button" value="submit" onclick="submitForm()"> </form> <script type="text/javascript"> function submitForm(){ var str ="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"; form1.action = "gb2312rs.jsp?qryParam1=" + encodeURIComponent(str) + "&qryParam2="+ encodeURIComponent(form1.textParam1.value) ; //form1.action = "http://localhost:8088/gb2312rs.jsp?qryParam1=" + encodeURIComponent(str) + "&qryParam2="+ encodeURIComponent(form1.textParam1.value) ; form1.submit(); } </script> </body> </html>
gb2312rs.jsp清单:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <head> <title>Insert title here</title> </head> <body> <% //这里一定要设置,且与浏览的提交时编码方式一样,否则乱码(默认Tomcat以iso8859-1解码) //且只能post提交方式的参数起使用,对URL后附带参数不起作用 request.setCharacterEncoding("GB2312"); String textParam1 = request.getParameter("textParam1"); String qryParam1 = request.getParameter("qryParam1"); System.out.println("textParam1=" + textParam1); System.out.println("qryParam1=" + qryParam1); System.out.println("qryParam1=" + (qryParam1==null?null:new String(qryParam1.getBytes("iso8859-1"),"utf-8"))); %> request.getParameter("textParam1")(<font color=red>GB2312编码->GB2312解码</font>):<br> <%=request.getParameter("textParam1")%> <br><hr> String(request.getParameter("textParam1").getBytes("iso8859-1"),"GB2312")(<font color=red>GB23128编码->ISO8859-1解码->ISO8859-1编码->GB2312解码</font>):<br> <%=new String(textParam1.getBytes("iso8859-1"),"gb2312")%> <br><hr> request.getParameter("qryParam1")(<font color=red>UTF-8编码->ISO8859-1解码</font>):<br> <%=qryParam1%> </body> </html>
1、以get方式提交表单
2、表单输入框中含有中文
3、Tomcat未设置URIEncoding与useBodyEncodingForURI
4、请求页面与结果页面<%@ page %>指令的contentType与pageEncoding编码方式都为GB2312
5、结果页面中request的编码方式为GB2312
GET /gb2312rs.jsp?textParam1=%D6%D0a+%7E%21@%23%24%25%5E%26*%28%29_%2B%7B%7D%7C%3A%5C%22+%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B%27%2C.%2F&fileParam= HTTP/1.1
|
码流分析:
http://localhost:8080/HttpStream/gb2312rs.jsp?textParam1=%D6%D0a+%7E%21@%23%24%25%5E%26*%28%29_%2B%7B%7D%7C%3A%5C%22+%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B%27%2C.%2F&fileParam=
1、直接以request.getParameter("textParam1")方式提取参数时,参数值为乱码。
2、URL后附加的参数未传递到服务器,解决办法:当提交表单时,如果表单URL还附加参数,则一定要以POST方式提交,否则是不能传递到服务器。
客户端编码:因为以GET方式发送参数,所以发送时采用浏览器的编码方式进行编码后再发送,这里的编码方式为请求页面设置的编码方式GB2312
服务器解码:因为Tomcat的<Connector/>标签未设置URIEncoding与useBodyEncodingForURI这两个属性,所以Tomact对GET方式传的参数采用默认的编码方式ISO8859-1进行编码。
最后结论:客户端编码方式(GB2312)与服务器解码方式(ISO8859-1)不同导致了乱码。
1、在结果页面读取附加参数时这样做:new String(request.getParameter("textParam").getBytes("iso8859-1"),"gb2312")
2、修改Tomcat配置文件server.xml中的<Connector ... URIEncoding="gb2312"... />也可。
3、修改Tomcat配置文件server.xml中的<Connector ... useBodyEncodingForURI="true"... />也可。
在浏览器地址栏中直接输入参数值时,如果我们把参数值手动转换成某种编码的%xx形式参数串后再发送时,这时浏览器不会再对%xx形式的参数值串进行任何编码,发送请求时直接会把%xx形式的参数值串原样传送到服务器,比如“中”字的 UTF-8编码为 E4 B8 AD,我们在编码方式为 GB2312 页面上的地址栏输入带参数的URL,且参数值为 %E4%B8%AD,浏览器此时会原样把%E4%B8%AD放在HTTP请求头里并传送到服务器,在传送到服务器端后,我们解码应该以UTF-8解码,而不是以GB2312编码方式来解码。
另一种情况就是直接输入参数值,不转换成%xx形式的参数串,提交时会先以浏览器当前编码方式来对值进行编码,然后以此种编码传送到服务器,不会以%xx形式串传递,比如当前浏览器的编码方式为GBP312,我们直接输入一个“国”字,则传递到服务器前先采用GBP312对“国”字进行编码(先前的“中”字都不会再编码了,因为已手动转换成%xx形式串了),然后传递该编码到服务器。下面来测试一下:
在地址栏中输入:
http://localhost:8088/HttpStream/gb2312.jsp?textParam1=%E4%B8%AD国
HTTP请求码流:
GET /HttpStream/gb2312.jsp?textParam1=%E4%B8%AD国 HTTP/1.1
|
从码流可以看出提交的到服务器的参数内容如下:
/HttpStream/gb2312.jsp?textParam1=%E4%B8%AD国
注:服务器在收到请求后,是以浏览器的编码方式GB2312编码方式来解码的,所以我们看到的“国”字才不是乱码。如果不以GB2312编码,则“国”字会是乱码。
最后来看看URLEncoder.encode(String s, String enc)机制:如果客户端URL中传递一个%xx形式串的参数 ,Web服务容器会自动调用此方式进行解码,至于解码时用到什么样的编码方式,它与JavaScript中的encodeURI、encodeURIComponent函数不一样,在编码上encodeURI、encodeURIComponent用的是UTF-8,是不能更改的。但Java里的URLEncoder.encode(String s, String enc)函数不一定是UTF-8,这里的编码与运行的环境是有关系的,默认情况下,在Tomcat5.5下是以ISO8859-1来解码URL中传递过来的参数的,但也可以设置URL的编码方式,比如Tomcat中设置URIEncoding与useBodyEncodingForURI属性是可以的。这里我们做个试,在地址栏中输入以下地址:
http://localhost:8080/HttpStream/gb2312rs.jsp?textParam1=%D6%D0国
注意,这里的%D6%D0为GBP312编码方式的“中”字,我们设置Tomcat中的URIEncoding="GB2312"后,输出结果页面如下:
从结果可以看出,URLEncoder.encode(String s, String enc)的编码方式是可以修改的,这里修改成了GB2312后,在我们不经过任何转换就可以得到正确的结果了。
从上面多种访问方式请求码流可以看出,
提交表单时(不管是POST还是GET),表单里输入元素的内容以及参数名(当然参数名我们一般不会取名成中文)都会先自动会以浏览器的编码方式进行编码 (a-z A-Z 0-9 +@*_-. 不进行编码),然后把编码转换成%xx(xx为两位的十六进制数)形式参数串后传送到服务器。