一、前言
最近在研究公司的一個項目,無意間注意起平時用的多但是沒怎么注意的一個問題,那就是瀏覽器往服務器傳送數據的時候到底是怎么編碼的?網上有篇文章寫得不錯,並且本人親測確實如文章所述,所以這邊特意轉載,並且分享一下我對這個細小知識點的總結,希望對剛入門的小白在解決前后端數據傳輸中,中文亂碼問題有所幫助~
二、正文
2.1 請求編碼
1)直接在地址欄中給出中文
請求數據是由客戶端瀏覽器發送服務器的,請求數據的編碼是由瀏覽器決定的。例如在瀏覽器地址欄中給出:http://localhost:8080/hello/AServlet?name=傳智,那么其中“傳智”是什么編碼的呢?不同瀏覽器使用不同的編碼,所以這是不確定的!
- IE:使用GB2312;
- FireFox:使用GB2312;
- Chrome:使用UTF-8;
通常沒有哪個應用要求用戶在瀏覽器地址欄中輸入請求數據的,所以大家只需了解一下即可
2)在頁面中發出請求
通常向服務器發送請求數據都需要先請求一個頁面,然后用戶在頁面中輸入數據。頁面中有超鏈接和表單,通過超鏈接和表單就可以向服務器發送數據了。因為頁面是服務器發送到客戶端瀏覽器的,所以這個頁面本身的編碼由服務器決定。而用戶在頁面中輸入的數據也是由頁面本身的編碼決定的。
index.html
<!DOCTYPE html>
<html>
<head>
<title>index.html</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<form action="/hello/servlet/AServlet">
名稱:<input type="text" name="name"/>
<input type="submit" value="提交"/>
</form>
<a href="/hello/servlet/AServlet?name=傳智">鏈接</a>
</body>
</html>
當用戶在index.html頁面中輸入數據時,都是UTF-8列表的。因為這個頁面本身就是UTF-8編碼的! 頁面的編譯就是頁面中輸入數據的編碼。
3)GET請求解讀編碼
當客戶端通過GET請求發送數據給服務器時,使用request.getParameter()獲取的數據是被服務器誤認為ISO-8859-1編碼的,也就是說客戶端發送過來的數據無論是UTF-8還是GBK,服務器都認為是ISO-8859-1,這就說明我們需要在使用request.getParameter()獲取數據后,再轉發成正確的編碼。
例如客戶端以UTF-8發送的數據,使用如下轉碼方式:
String name = request.getParameter(“name”); name = new String(name.getBytes(“iso-8859-1”), “utf-8”);
4)POST請求解讀編碼
當客戶端通過POST請求發送數據給服務器時,可以在使用request.getParameter()獲取請求參數之前先通過request.setCharacterEncoding()來指定編碼,然后再使用reuqest.getParameter()方法來獲取請求參數,那么就是用指定的編碼來讀取了。 也就是說,如果是POST請求,服務器可以指定編碼!但如果沒有指定編碼,那么默認還是使用ISO-8859-1來解讀。
request.setCharacterEncoding(“utf-8”);
String name = request.getParameter(“name”);
2.2 響應編碼
響應:服務器發送給客戶端數據!響應是由response對象來完成,如果響應的數據不是字符數據,那么就無需去考慮編碼問題。當然,如果響應的數據是字符數據,那么就一定要考慮編碼的問題了。
response.getWriter().print(“傳智”);
上面代碼因為沒有設置repsonse.getWriter()字符流的編碼,所以服務器使用默認的編碼(ISO-8859-1)來處理,因為ISO-8859-1不支持中文,所以一定會出現編碼的。
所以在使用response.getWriter()發送數據之前,一定要設置response.getWriter()的編碼,這需要使用response.setCharacterEncoding()方法:
response.setCharacterEncoding(“utf-8”);
response.getWriter().print(“傳智”);
上面代碼因為在使用response.getWriter()輸出之前已經設置了編碼,所以輸出的數據為utf-8編碼。但是,因為沒有告訴瀏覽器使用什么編碼來讀取響應數據,所以很可能瀏覽器會出現錯誤的解讀,那么還是會出現亂碼的。當然,通常瀏覽器都支持來設置當前頁面的編碼,如果用戶在看到編碼時,去設置瀏覽器的編碼,如果設置的正確那么亂碼就會消失。但是我們不能讓用戶總去自己設置編碼,而且應該直接通知瀏覽器,服務器發送過來的數據是什么編碼,這樣瀏覽器就直接使用服務器告訴他的編碼來解讀!這需要使用content-type響應頭。
response.setContentType(“text/html;charset=utf-8”);
response.getWriter().print(“傳智”);
上面代碼使用setContentType()方法設置了響應頭content-type編碼為utf-8,這不只是在響應中添加了響應頭,還等於調用了一次response.setCharacterEncoding(“utf-8”),也就是說,通過我們只需要調用一次response.setContentType(“text/html;charset=utf-8”)即可,而無需再去調用response.setCharacterEncoding(“utf-8”)了。
在靜態頁面中,使用來設置content-type響應頭,例如:
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
2.3 URL編碼
通過頁面傳輸數據給服務器時,如果包含了一些特殊字符是無法發送的。這時就需要先把要發送的數據轉換成URL編碼格式,再發送給服務器。
其實需要我們自己動手給數據轉換成URL編碼的只有GET超鏈接,因為表單發送數據會默認使用URL編碼,也就是說,不用我們自己來編碼。
例如:“傳智”這兩個字通過URL編碼后得到的是:“%E4%BC%A0%E6%99%BA”。URL編碼是先需要把“傳智”轉換成字節,例如我們現在使用UTF-8把“傳智”轉換成字符,得到的結果是:“[-28, -68, -96, -26, -103, -70]”,然后再把所有負數加上256,得到[228, 188, 160, 230, 153, 186],再把每個int值轉換成16進制,得到[E4, BC, A0, E6, 99, BA],最后再每個16進制的整數前面加上“%”。
通過URL編碼,把“傳智”轉換成了“%E4%BC%A0%E6%99%BA”,然后發送給服務器!服務器會自動識別出數據是使用URL編碼過的,然后會自動把數據轉換回來。
當然,在頁面中我們不需要自己去通過上面的過程把“傳智”轉換成“%E4%BC%A0%E6%99%BA”,而是使用Javascript來完成即可。當后面我們學習了JSP后,就不用再使用Javascript了。
<script type="text/javascript">
function _go() {
location = "/day05_2/AServlet?name=" + encodeURIComponent("傳智+播客");
}
</script>
<a href="javascript:_go();">鏈接</a>
因為URL默認只支持ISO-8859-1,這說明在URL中出現中文和一些特殊字符可能無法發送到服務器。所以我們需要對包含中文或特殊字符的URL進行URL編碼。
服務器會自動識別數據是否使用了URL編碼,如果使用了服務器會自動把數據解碼,無需我們自己動手解碼。
個人總結:首先我們需要明確,在這個亂碼問題中,有兩個對象一個是客戶端,一個是服務端,服務端就是我們自己部署的程序,客戶端就是我們用戶的瀏覽器。通信過程中涉及客戶端對將要發送數據的編碼(解碼),以及服務端對收到的數據進行解碼(編碼)問題,只要兩邊編碼/解碼的字符集一致,就不會出現亂碼問題。在客戶端編碼過程中,如果是在地址欄中直接輸入參數,比如name=傑克&age=12的方式進行數據傳輸,那么不同瀏覽器會采用不同的字符集,對數據進行編碼(編碼的結果就是字節,並且在底層進行傳輸),在服務器端,針對不同的提交方式,不同的處理(get和post的處理方式不同),默認不進行處理的話,httpservletrequest.getParameter("xxxx"),獲取的值是ISO-8859-1編碼的字符(不管你在瀏覽器發送出來是以UTF-8編碼的還是GBK等進行編碼的數據,在服務端不進行編碼設置的話,都是以ISO-8859-1編碼,對傳輸過來的字節進行顯示),如果存在中文,那么就會亂碼,對get和post提交的數據,如何處理亂碼(這邊需要特意說明一點:在解碼過程中,並沒有對傳輸過來的字節進行改變,只是在顯示的時候,對字節存儲的數據進行了映射而已):
get方式提交的數據(這邊假設瀏覽器端用的是UTF-8進行編碼傳遞過來的):
String name = request.getParameter(“name”); name = new String(name.getBytes(“iso-8859-1”), “utf-8”);
post方式提交的數據(這邊假設瀏覽器端用的是UTF-8進行編碼傳遞過來的):
request.setCharacterEncoding(“utf-8”);
String name = request.getParameter(“name”);
三、參考鏈接
http://blog.csdn.net/xiaoyiaoyou/article/details/45147495
四、聯系本人
為方便沒有博客園賬號的讀者交流,特意建立一個企鵝群(純公益,非利益相關),讀者如果有對博文不明之處,歡迎加群交流:261746360,小杜比亞-博客園。
