服务器与浏览器数据传输过程中编码问题
一、前言
最近在研究公司的一個(gè)項(xiàng)目,無意間注意起平時(shí)用的多但是沒怎么注意的一個(gè)問題,那就是瀏覽器往服務(wù)器傳送數(shù)據(jù)的時(shí)候到底是怎么編碼的?網(wǎng)上有篇文章寫得不錯(cuò),并且本人親測(cè)確實(shí)如文章所述,所以這邊特意轉(zhuǎn)載,并且分享一下我對(duì)這個(gè)細(xì)小知識(shí)點(diǎn)的總結(jié),希望對(duì)剛?cè)腴T的小白在解決前后端數(shù)據(jù)傳輸中,中文亂碼問題有所幫助~
二、正文
2.1?請(qǐng)求編碼?
1)直接在地址欄中給出中文?
請(qǐng)求數(shù)據(jù)是由客戶端瀏覽器發(fā)送服務(wù)器的,請(qǐng)求數(shù)據(jù)的編碼是由瀏覽器決定的。例如在瀏覽器地址欄中給出:http://localhost:8080/hello/AServlet?name=傳智,那么其中“傳智”是什么編碼的呢?不同瀏覽器使用不同的編碼,所以這是不確定的!
- IE:使用GB2312;
- FireFox:使用GB2312;
- Chrome:使用UTF-8;
通常沒有哪個(gè)應(yīng)用要求用戶在瀏覽器地址欄中輸入請(qǐng)求數(shù)據(jù)的,所以大家只需了解一下即可
?
2)在頁面中發(fā)出請(qǐng)求?
通常向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)都需要先請(qǐng)求一個(gè)頁面,然后用戶在頁面中輸入數(shù)據(jù)。頁面中有超鏈接和表單,通過超鏈接和表單就可以向服務(wù)器發(fā)送數(shù)據(jù)了。因?yàn)轫撁媸欠?wù)器發(fā)送到客戶端瀏覽器的,所以這個(gè)頁面本身的編碼由服務(wù)器決定。而用戶在頁面中輸入的數(shù)據(jù)也是由頁面本身的編碼決定的。?
index.html
當(dāng)用戶在index.html頁面中輸入數(shù)據(jù)時(shí),都是UTF-8列表的。因?yàn)檫@個(gè)頁面本身就是UTF-8編碼的!?頁面的編譯就是頁面中輸入數(shù)據(jù)的編碼。
?
3)GET請(qǐng)求解讀編碼?
當(dāng)客戶端通過GET請(qǐng)求發(fā)送數(shù)據(jù)給服務(wù)器時(shí),使用request.getParameter()獲取的數(shù)據(jù)是被服務(wù)器誤認(rèn)為ISO-8859-1編碼的,也就是說客戶端發(fā)送過來的數(shù)據(jù)無論是UTF-8還是GBK,服務(wù)器都認(rèn)為是ISO-8859-1,這就說明我們需要在使用request.getParameter()獲取數(shù)據(jù)后,再轉(zhuǎn)發(fā)成正確的編碼。?
例如客戶端以UTF-8發(fā)送的數(shù)據(jù),使用如下轉(zhuǎn)碼方式:
4)POST請(qǐng)求解讀編碼?
當(dāng)客戶端通過POST請(qǐng)求發(fā)送數(shù)據(jù)給服務(wù)器時(shí),可以在使用request.getParameter()獲取請(qǐng)求參數(shù)之前先通過request.setCharacterEncoding()來指定編碼,然后再使用reuqest.getParameter()方法來獲取請(qǐng)求參數(shù),那么就是用指定的編碼來讀取了。?也就是說,如果是POST請(qǐng)求,服務(wù)器可以指定編碼!但如果沒有指定編碼,那么默認(rèn)還是使用ISO-8859-1來解讀。?
?
2.2 響應(yīng)編碼?
響應(yīng):服務(wù)器發(fā)送給客戶端數(shù)據(jù)!響應(yīng)是由response對(duì)象來完成,如果響應(yīng)的數(shù)據(jù)不是字符數(shù)據(jù),那么就無需去考慮編碼問題。當(dāng)然,如果響應(yīng)的數(shù)據(jù)是字符數(shù)據(jù),那么就一定要考慮編碼的問題了。?
上面代碼因?yàn)闆]有設(shè)置repsonse.getWriter()字符流的編碼,所以服務(wù)器使用默認(rèn)的編碼(ISO-8859-1)來處理,因?yàn)镮SO-8859-1不支持中文,所以一定會(huì)出現(xiàn)編碼的。?
所以在使用response.getWriter()發(fā)送數(shù)據(jù)之前,一定要設(shè)置response.getWriter()的編碼,這需要使用response.setCharacterEncoding()方法:?
上面代碼因?yàn)樵谑褂胷esponse.getWriter()輸出之前已經(jīng)設(shè)置了編碼,所以輸出的數(shù)據(jù)為utf-8編碼。但是,因?yàn)闆]有告訴瀏覽器使用什么編碼來讀取響應(yīng)數(shù)據(jù),所以很可能瀏覽器會(huì)出現(xiàn)錯(cuò)誤的解讀,那么還是會(huì)出現(xiàn)亂碼的。當(dāng)然,通常瀏覽器都支持來設(shè)置當(dāng)前頁面的編碼,如果用戶在看到編碼時(shí),去設(shè)置瀏覽器的編碼,如果設(shè)置的正確那么亂碼就會(huì)消失。但是我們不能讓用戶總?cè)プ约涸O(shè)置編碼,而且應(yīng)該直接通知瀏覽器,服務(wù)器發(fā)送過來的數(shù)據(jù)是什么編碼,這樣瀏覽器就直接使用服務(wù)器告訴他的編碼來解讀!這需要使用content-type響應(yīng)頭。?
response.setContentType(“text/html;charset=utf-8”); response.getWriter().print(“傳智”);上面代碼使用setContentType()方法設(shè)置了響應(yīng)頭content-type編碼為utf-8,這不只是在響應(yīng)中添加了響應(yīng)頭,還等于調(diào)用了一次response.setCharacterEncoding(“utf-8”),也就是說,通過我們只需要調(diào)用一次response.setContentType(“text/html;charset=utf-8”)即可,而無需再去調(diào)用response.setCharacterEncoding(“utf-8”)了。
在靜態(tài)頁面中,使用來設(shè)置content-type響應(yīng)頭,例如:
<meta http-equiv="content-type" content="text/html; charset=UTF-8">?
2.3 URL編碼?
通過頁面?zhèn)鬏敂?shù)據(jù)給服務(wù)器時(shí),如果包含了一些特殊字符是無法發(fā)送的。這時(shí)就需要先把要發(fā)送的數(shù)據(jù)轉(zhuǎn)換成URL編碼格式,再發(fā)送給服務(wù)器。?
其實(shí)需要我們自己動(dòng)手給數(shù)據(jù)轉(zhuǎn)換成URL編碼的只有GET超鏈接,因?yàn)楸韱伟l(fā)送數(shù)據(jù)會(huì)默認(rèn)使用URL編碼,也就是說,不用我們自己來編碼。?
例如:“傳智”這兩個(gè)字通過URL編碼后得到的是:“%E4%BC%A0%E6%99%BA”。URL編碼是先需要把“傳智”轉(zhuǎn)換成字節(jié),例如我們現(xiàn)在使用UTF-8把“傳智”轉(zhuǎn)換成字符,得到的結(jié)果是:“[-28, -68, -96, -26, -103, -70]”,然后再把所有負(fù)數(shù)加上256,得到[228, 188, 160, 230, 153, 186],再把每個(gè)int值轉(zhuǎn)換成16進(jìn)制,得到[E4, BC, A0, E6, 99, BA],最后再每個(gè)16進(jìn)制的整數(shù)前面加上“%”。?
通過URL編碼,把“傳智”轉(zhuǎn)換成了“%E4%BC%A0%E6%99%BA”,然后發(fā)送給服務(wù)器!服務(wù)器會(huì)自動(dòng)識(shí)別出數(shù)據(jù)是使用URL編碼過的,然后會(huì)自動(dòng)把數(shù)據(jù)轉(zhuǎn)換回來。
當(dāng)然,在頁面中我們不需要自己去通過上面的過程把“傳智”轉(zhuǎn)換成“%E4%BC%A0%E6%99%BA”,而是使用Javascript來完成即可。當(dāng)后面我們學(xué)習(xí)了JSP后,就不用再使用Javascript了。
<script type="text/javascript">function _go() {location = "/day05_2/AServlet?name=" + encodeURIComponent("傳智+播客");}</script> <a href="javascript:_go();">鏈接</a>因?yàn)閁RL默認(rèn)只支持ISO-8859-1,這說明在URL中出現(xiàn)中文和一些特殊字符可能無法發(fā)送到服務(wù)器。所以我們需要對(duì)包含中文或特殊字符的URL進(jìn)行URL編碼。?
服務(wù)器會(huì)自動(dòng)識(shí)別數(shù)據(jù)是否使用了URL編碼,如果使用了服務(wù)器會(huì)自動(dòng)把數(shù)據(jù)解碼,無需我們自己動(dòng)手解碼。
?
個(gè)人總結(jié):首先我們需要明確,在這個(gè)亂碼問題中,有兩個(gè)對(duì)象一個(gè)是客戶端,一個(gè)是服務(wù)端,服務(wù)端就是我們自己部署的程序,客戶端就是我們用戶的瀏覽器。通信過程中涉及客戶端對(duì)將要發(fā)送數(shù)據(jù)的編碼(解碼),以及服務(wù)端對(duì)收到的數(shù)據(jù)進(jìn)行解碼(編碼)問題,只要兩邊編碼/解碼的字符集一致,就不會(huì)出現(xiàn)亂碼問題。在客戶端編碼過程中,如果是在地址欄中直接輸入?yún)?shù),比如name=杰克&age=12的方式進(jìn)行數(shù)據(jù)傳輸,那么不同瀏覽器會(huì)采用不同的字符集,對(duì)數(shù)據(jù)進(jìn)行編碼(編碼的結(jié)果就是字節(jié),并且在底層進(jìn)行傳輸),在服務(wù)器端,針對(duì)不同的提交方式,不同的處理(get和post的處理方式不同),默認(rèn)不進(jìn)行處理的話,httpservletrequest.getParameter("xxxx"),獲取的值是ISO-8859-1編碼的字符(不管你在瀏覽器發(fā)送出來是以UTF-8編碼的還是GBK等進(jìn)行編碼的數(shù)據(jù),在服務(wù)端不進(jìn)行編碼設(shè)置的話,都是以ISO-8859-1編碼,對(duì)傳輸過來的字節(jié)進(jìn)行顯示),如果存在中文,那么就會(huì)亂碼,對(duì)get和post提交的數(shù)據(jù),如何處理亂碼(這邊需要特意說明一點(diǎn):在解碼過程中,并沒有對(duì)傳輸過來的字節(jié)進(jìn)行改變,只是在顯示的時(shí)候,對(duì)字節(jié)存儲(chǔ)的數(shù)據(jù)進(jìn)行了映射而已):
get方式提交的數(shù)據(jù)(這邊假設(shè)瀏覽器端用的是UTF-8進(jìn)行編碼傳遞過來的):
String name = request.getParameter(“name”);name = new String(name.getBytes(“iso-8859-1”), “utf-8”);post方式提交的數(shù)據(jù)(這邊假設(shè)瀏覽器端用的是UTF-8進(jìn)行編碼傳遞過來的):
request.setCharacterEncoding(“utf-8”); String name = request.getParameter(“name”);三、參考鏈接
? ? ? ?http://blog.csdn.net/xiaoyiaoyou/article/details/45147495
四、聯(lián)系本人
為方便沒有博客園賬號(hào)的讀者交流,特意建立一個(gè)企鵝群(純公益,非利益相關(guān)),讀者如果有對(duì)博文不明之處,歡迎加群交流:261746360,小杜比亞-博客園。
轉(zhuǎn)載于:https://www.cnblogs.com/xdouby/p/8308915.html
總結(jié)
以上是生活随笔為你收集整理的服务器与浏览器数据传输过程中编码问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Putty常用属性设置
- 下一篇: OSPF(Open Shortest P