测试环境
服务器:Tomcat5.5
浏览器:Firfox3.5、IE7.0
HTTP码流服务端截取程序
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" οnclick="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图:
请求页面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= |
从上面码流分析可知
- 提交请求为编码方式为GB2312的页面表单时,参数qryParam1与参数qryParam2都是UTF-8的编码格式,而不是以GBP312方式编码的,因此encodeURIComponent()函数是固定以UTF-8编码的,它不会受到当前浏览器的编码影响。
- 经过encodeURIComponent()函数编码后的附加参数内容会以%xx形式串显示在地址栏中。
- POST方式的HTTP头与HTTP体是用一个回车换行来分隔的。
- POST方式提交表单,如果URL后面还附还参数,也会提交到服务器,且放在HTTP头部,其他表单输入元素会放在HTTP体里传送。
- POST方式提交表单时,在头部会有 Content-Type: application/x-www-form-urlencoded 这样一个头信息,而GET方式提交的表单是不会有该头信息的,也不会有HTTP请求体。
- POST方式提交时,表单里的元素值会先用浏览器的编码方式时行编码(a-z A-Z 0-9 +@*_-. 不进行编码),然后把编码转换成%xx(xx为两位的十六进制)形式参数串后传送到服务器。
出现如下问题:
直接通过 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" οnclick="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码流可以看出:
- 未经过encodeURIComponent()函数编码的URL附加参数会原样显示在地址样中,如上面的“中”字,并且也不会以%xx形式串传送到服务器,而是经浏览器编码后直接把编码传递服务器端。
- POST方式提交时,表单里的元素值会先用浏览器的编码方式时行编码(a-z A-Z 0-9 +@*_-. 不进行编码),然后把编码转换成%xx(xx为两位的十六进制)形式参数串后传送到服务器。
问题出现时环境:
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" οnclick="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
|
码流分析:
- GET方式提交表单时,如果表单URL附加参数,这些参数是不能传递到服务器端去的,相反表单里的元素会附加在URL后面并传送到服务器,所以当提交表单时,如果表单URL还附加参数,则一定要以POST方式提交,否则是不能传递到服务器。
- GET方式提交的请求HTTP头里没有 Content-Type: application/x-www-form-urlencoded 头信息。
- GET提交时,表单里的元素值会先用浏览器的编码方式时行编码,然后把编码转换成%xx形式的串。
结果页面:
地址栏显示:
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()
最后来看看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为两位的十六进制数)形式参数串后传送到服务器。