HttpClient資料傳輸的編碼方式
約定
1. HttpClient版本:HttpClient4.1
2. 服務端中介軟體:tomcat7
HttpGet請求的URI編碼設定
1. 問題及原因
使用HttpClient的HttpGet方法進行get請求時,如果請求的URI中含有中文引數,則請求的服務端容易出現亂碼問題。出現亂碼的原因主要是因為請求URI中中文引數的編碼和tomcat設定的編碼不一致,導致亂碼。
2. 解決方案
1) 設定tomcat編碼
首先必須確定tomcat採用何種編碼,才能確定請求的URI設定為何種編碼。Tomcat的編碼設定在tomcat的配置檔案中設定,位置為: apache-tomcat-7.0.11\conf\server.xml,找到如下配置:
<Connector
port="8181"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="GB2312"/>
Tomcat預設是沒有對URIEncoding進行配置,tomcat會採用預設的編碼,一般是ISO-8859-1。
2) 設定URI編碼
知道了tomcat(請求服務端)的編碼方式,使用HttpGet請求時就需要對URI中的中文引數進行編碼。上面設定了URIEncoding=”GB2312”,所以我們需要對中文引數進行GB2312的URI編碼。例如:
HttpClient http = new DefaultHttpClient();
String url = "http://127.0.0.1:8181/hello/httpclient?test=" + URLEncoder.encode("中文", "utf-8");
HttpGet get = new HttpGet(url);
HttpResponse response = http.execute(get);
這樣服務端接收到HttpGet的請求,中文引數就不會出現亂碼了。
HttpPost請求的URI及資料編碼
1. 問題及原因
使用HttpClient的HttpPost方法進行Post請求時與Get不同的是,Post不僅有URI引數,還有請求body資料,如果這兩種資料出現中文都有可能出現亂碼。其中URI引數出現亂碼的原因與上述Get方法出現亂碼的原因相同,這裡就不在敘述。Body資料出現亂碼的原因則是請求資料的編碼方式與服務端程式的解碼方式不一致導致。
2. 解決方案
1) HttpClient端Post請求的編碼設定
HttpClient http = new DefaultHttpClient();
String url = "http://127.0.0.1:8181/hello/httpclient?test=" + URLEncoder.encode("中文", "gb2312");
HttpPost post = new HttpPost(url);
JSONObject params = new JSONObject();
params.put("a", "測試1");
params.put("b", "測試2");
System.out.println(params.toString());
StringEntity entity = new StringEntity(params.toString(),"gb2312");
entity.setContentType("application/json;charset=gb2312");
post.setEntity(entity);
HttpResponse response = http.execute(post);
程式碼中
StringEntity entity = new StringEntity(params.toString(),"gb2312");
設定了以gb2312編碼對資料進行編碼。
如果不傳入編碼方式
new StringEntity(params.toString())
則會以預設的編碼方式進行編碼,一般是ISO-8859-1,這種編碼無法表示中文字元,所以如果不進行設定,中文會出現亂碼。
entity.setContentType("application/json;charset=gb2312");
設定請求的content-type,如果不設定,預設會根據
new StringEntity(params.toString(),"gb2312");
的編碼方式來設定,text/plain;charset= gb2312
2) 服務端程式碼的解碼
public void httpclientPost(HttpServletRequest req,HttpServletResponse resp) throws Exception {
String a = req.getParameter("test");
System.out.println("URI引數: test=" + a);
System.out.println("req-content-type: " + req.getContentType() + "\ncharset: " + req.getCharacterEncoding());
InputStream in = req.getInputStream();
List<Byte> dataList = new ArrayList<Byte>();
byte[] buffer = new byte[8*1024];
while(in.read(buffer)>0){
for(int i=0; i<buffer.length;++i){
dataList.add(buffer[i]);
}
};
byte[] data = new byte[dataList.size()+1];
for(int i=0; i<dataList.size(); ++i){
data[i] = dataList.get(i);
}
System.out.println(new String(data,req.getCharacterEncoding()));
}
執行結果如下:
URI引數: test=中文
req-content-type: application/json;charset=gb2312
charset: gb2312
{"a":"測試1","b":"測試2"}
- 1
- 2
- 3
- 4
可以看到
new String(data,req.getCharacterEncoding())
這裡使用請求中的資料編碼方式來從資料流中生成對應的字串,這樣得到的中文資料就不會出現亂碼了。
HttpClient請求響應資料中文亂碼
1. 問題及原因
使用HttpClient的Get或Post方法請求得到的響應資料中的中文出現亂碼。出現亂碼的原因主要是因為對得到的響應資料解碼方式和服務端的編碼方式不一致。
2. 解決方案
1) 服務端設定返回資料的編碼方式
public void httpclient(HttpServletRequest req,HttpServletResponse resp) throws Exception {
resp.setContentType("text/html;charset=gb2312");
resp.setCharacterEncoding("gb2312");
resp.getWriter().println("response返回中文");
}
setContentType與setCharacterEncoding都可以設定編碼方式,但是必須呼叫setContentType來設定content-type,後面會說明原因。
2) HttpClient請求得到響應後解碼
HttpResponse response = http.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
Header header = entity.getContentType();
String strResp = EntityUtils.toString(response.getEntity());
System.out.println(strResp);
}
這裡的EntityUtils.toString()方法將得到的資料轉換為字串,那麼就必須要考慮所使用的字元編碼。如果在EntityUtils.toString()方法中沒有顯式的傳入字元編碼方式的引數,那麼該方法就會根據response.getEntity()的ContentType的編碼方式來進行解碼。Header header = entity.getContentType();這個方法就可以獲取entity的content-type。但是,如果服務端沒有設定Content-Type,也就是在上面的服務端程式中沒有呼叫setContentType()方法,那麼這裡獲取的header將是null,也就是說無法獲取到entity的Content-Type。那麼EntityUtils.toString()方法也就無法知道服務端傳過來的資料採用的何種編碼方式,就會採用預設的ISO-8859-1編碼方式進行轉換,這樣中文就會出現亂碼。當然,也可以顯式將編碼方式傳入EntityUtils.toString()函式中,但是必須確保傳入的編碼方式與服務端設定的返回資料的編碼方式相同,EntityUtils.toString(response.getEntity(),”gb2312”);