请求测试代码
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
public class HttpCharsetTest {
public static void main(String[] args) throws Exception {
String strUrl = "http://localhost:8080/front_new/rproxy/123/456";
Map<String, String> requestParams = new HashMap<String, String>();
requestParams.put("testkey", "测试");
int i = 0;
NameValuePair[] nvps = new NameValuePair[requestParams.size()];
for (String strKey : requestParams.keySet()) {
NameValuePair nvp = new NameValuePair();
nvp.setName(strKey);
nvp.setValue(requestParams.get(strKey));
nvps[i] = nvp;
i++;
}
PostMethod postMethod = null;
HttpClient httpClient = new HttpClient();
// 设置超时时间
httpClient.getHttpConnectionManager().getParams()
.setConnectionTimeout(30000);
httpClient.getHttpConnectionManager().getParams().setSoTimeout(30000);
postMethod = new PostMethod(strUrl);
postMethod.setRequestBody(nvps);
// postMethod.addRequestHeader("Content-Type", "text/html;charset=UTF-8");
// httpClient.getParams().setContentCharset("UTF-8");
// postMethod.getParams().setContentCharset("UTF-8");
httpClient.executeMethod(postMethod);
String resultStr = postMethod.getResponseBodyAsString();
System.err.println(resultStr);
}
}
httpClient请求编码设置
postMethod.addRequestHeader("Content-Type", "text/html;charset=UTF-8");
httpClient.getParams().setContentCharset("UTF-8");
postMethod.getParams().setContentCharset("UTF-8");
以上三种方式,下面的设置方法会覆盖上面的设置。
内容查看代码
获取请求完整内容:
byte[] data=IOUtils.readBytesFromStream(request.getInputStream());
String strData=new String(data);
String strDecode=URLDecoder.decode(strData, "UTF-8");
内容测试
不同的httpClient请求编码,参数内容“测试”二字,方式post,strData
内容如下:
不设置字符集
testkey=%3F%3F
UTF-8字符集
testkey=%E6%B5%8B%E8%AF%95
GBK字符集
testkey=%B2%E2%CA%D4
如果不设置字符集,每个汉字编码成占一个字节的编码,没找到任何一个编码与之对应URLDecoder.decode
后是乱码。
UTF-8编码成三个字节一个汉字,GBK编码成二个字节一个汉字,用对应编码URLDecoder.decode
后都是正常汉字。
获取请求参数
在获取参数调用request.getParameter
前,要设置字符编码:
request.setCharacterEncoding("UTF-8");
可以通过设置spring的filter统一设置编码:
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
spring的filter的实质:
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
request.setCharacterEncoding(this.encoding);
if (this.forceEncoding) {
response.setCharacterEncoding(this.encoding);
}
}
filterChain.doFilter(request, response);
}
encoding的作用相当于:
request.setCharacterEncoding
forceEncoding为true的作用相当于:
request.setCharacterEncoding(“”);
response.setCharacterEncoding(“”);
乱码详解
一个Http请求的数据大致包括URI、Header、和Body三个部分。这三个部分都需要encoding,不过一般只涉及到URI和Body,因此就不讨论Header了。
GET的请求参数在QueryString中,是URI的一部分。因此,对于GET请求,我们需要关注,URI是如何encoding的?
POST的请求参数在Body中,因此,对于POST请求,我们则需要关注,Body是如何encoding的?
麻烦的是,URI和Body的charset还可以不一样,使用不同的方法进行设置和配置。
Tomcat如何设置charset
Tomcat通过URI的charset来设置QueryString的charset。我们可以在Tomcat根目录下conf/server.xml 中进行配置。
<Connector
URIEncoding="UTF-8"
useBodyEncodingForURI="true"
acceptCount="100"
connectionTimeout="20000"
disableUploadTimeout="true"
enableLookups="false"
maxHttpHeaderSize="8192"
maxSpareThreads="75"
maxThreads="150"
minSpareThreads="25"
port="8080"
redirectPort="8443"/>
URIEncoding属性就是URI的charset,上述配置表示 Tomcat认为URI的charset就是UTF-8。如果HttpClient也使用UTF-8作为QueryString的charset,那么Tomcat就可以正确decoding。详情可以参考org.apache.tomcat.util.http.Parameters类的handleQueryParameters的方法。
Tomcat在启动的过程中,如果从conf/server.xml中读取到URIEncoding属性,就会设置queryStringEncoding的值。当Tomcat处理HTTP请求时,上述方法就会被调用。
默认的server.xml是没有配置URIEncoding属性的,需要我们手动设置 。如果没有设置,Tomcat就会采用一种称为“fast conversion”的方式解析QueryString。详情可以参考org.apache.tomcat.util.http.Parameter类的urlDecode方法。
useBodyEncodingForURI是与URI charset相关的另一个属性。如果该属性的值为true,则Tomcat将使用Body的charset作为URI的charset。如果Tomcat没有设置Body的charset,那么将使用HTTP请求Content-Type Header中的charset。如果HTTP请求中没有设置Content-Type Header,则使用ISO-8859-1作为默认charset。详情参见org.apache.catalina.connector.Request的parseParmeters方法
默认的server.xml是没有配置useBodyEncodingForURI属性的,需要我们手动设置 。如果没有设置,Tomcat则认为其值为false。需要注意的是,如果URIEncoding和useBodyEncodingForURI同时设置,而且Body的charset已经设置,那么将以Body的charset为准 。
设置POST请求Body的charset:
设置Body charset的方法很简单,只要调用javax.servlet.ServletRequest接口的setCharacterEncoding方法即可,比如request.setCharacterEncoding(“UTF-8”)。需要注意的是,该方法必须在读取任何请求参数之前调用,才有效果。
也就是说,我们只有在调用getParameter或getReader方法之前,调用setsetCharacterEncoding方法,设置的charset才能奏效。
响应数据的charset:
设置响应数据charset的方法很简单,只要调用javax.servlet.ServletResponse接口的setContentType或setCharacterEncoding方法即可,比如response.setContentType(“text/html;charset=UTF-8”)或response.setCharacterEncoding(“UTF-8”)。
如果Servlet正确设置了响应数据的charset,那么HTTP响应数据中就会包含Content-Type Header。HttpClient的getResponseBodyAsString方法就可以正确decoding响应数据。