JSP編碼以及亂碼解決總結
首先,說說JSP/Servlet中的幾個編碼的作用:
1.<%@pagepageEncoding="UTF-8" %>作用:
* 告訴JSP編譯器在將JSP檔案編譯成Servlet時使用的編碼。通常,在JSP內部定義的字串(直接在JSP中定義,而不是從瀏覽器提交的資料)出現亂碼時,很多都是由於該引數設定錯誤引起的。
例如,你的JSP檔案是以GBK為編碼儲存的 (右擊jsp-->Properties --> Text file encoding設定成與pageEncoding不一致時,就有亂碼),而在JSP中卻指定pageEncoding="UTF-8",就會引起JSP內部定義的字串為亂碼。
* 當JSP中不指定contentType引數,也不使用response.setCharacterEncoding方法時,指定對伺服器響應進行重新編碼的編碼
2.<%@pagecontentType="text/html;charset=GBK" %>response.setCharacterEncoding("UTF-8")作用:
* 指定對伺服器響應進行重新編碼的編碼,伺服器在將資料傳送到瀏覽器前,對資料進行重新編碼。
* 瀏覽器也是根據這個引數來對其接收到的資料進行解碼,對其傳送的請求引數進行編碼(如果引數有:<%=URLEncoder.encode("測試","GBK") %>或javascript:encodeURI("測試")則以後者為準),
自己可以在IE的選單中選擇:檢視(V) --> 編碼(D)檢視得知瀏覽器的編碼(解碼)和<%@page contentType="text/html;charset=GBK" %> response.setCharacterEncoding("UTF-8")設定的編碼是一致的。
因此:只要響應結果不是亂碼,則瀏覽器顯示結果一定不會是亂碼(伺服器對響應編碼和瀏覽器對響應解碼的編碼是一樣的)。出現亂碼的情況是:瀏覽器編碼 ---> 伺服器解碼得到的結果是亂碼 ---> 造成響應有亂碼 --> 瀏覽器顯示亂碼
3.<metahttp-equiv="Content-Type" content="text/html;charset=UTF-8">作用:
* 控制瀏覽器的以何種編碼顯示網頁的內容
* 與2中page指令設定的區別是,meta設定的是瀏覽器解釋,page設定的是服務端解釋
4.request.setCharacterEncoding("charset")作用:
* 設定對客戶端請求引數進行解碼所使用的編碼.
* 只對兩種請求引數提交方式有效:
* POST表單提交
* GET提交(url或GET表單提交), 此時要求配置
server.xml的<Connector>標籤的屬性:useBodyEncodingForURI="true"
5.request.getParameter("param")和request.getParameterValues("param")
所獲取到的引數資料都是經過伺服器解碼後的資料
接著說下發送請求到返回響應整個編碼解碼過程:
1.瀏覽器對請求編碼--> 伺服器(容器)對請求解碼 ---> 伺服器對響應編碼 ---> 瀏覽器對響應解碼
瀏覽器編碼 ---> 容器解碼
瀏覽器編碼預設是使用response.setCharacterEncoding—contentType—pageEncoding的優先順序指定的編碼的,但當對某些引數使用<%=URLEncoder.encode("測試","GBK") %>或javascript:encodeURI("測試")來對引數編碼時,會覆蓋預設編碼,即瀏覽器這些特定的引數編碼以<%=URLEncoder.encode("測試","GBK") %>或javascript:encodeURI("測試")為準
注意:javascript的編碼方式:encodeURI(..)和encodeURIComponent(..)是對其引數進行UTF-8編碼的
2.瀏覽器最終顯示響應結果出現亂碼是因為在過程:
瀏覽器對請求編碼 --> 伺服器(容器)對請求解碼,出現亂碼。
原因是:伺服器對響應編碼和瀏覽器對響應解碼所使用的編碼都是相同的,為response.setCharacterEncoding—contentType指定的編碼,而瀏覽器對請求引數的編碼 和 伺服器對請求引數解碼 所使用的編碼如果不一致,就會造成亂碼
3.對中文解碼,無論使用什麼解碼方式都是中文。中文只有經過編碼,再解碼才會出現亂碼的可能,如果起始編碼和末尾解碼所使用的編碼一致,就不會出現亂碼
4.瀏覽器編解碼說明:
瀏覽器在接收或傳送資料時,會對URL和引數會進行URL解碼(接收)或編碼(傳送),所使用的編碼為:<%@pagecontentType="text/html;charset=GBK" %>或response.setCharacterEncoding("UTF-8")指定的編碼
5.伺服器編解碼說明:
* 伺服器傳送資料時,按照response.setCharacterEncoding—contentType—pageEncoding的優先順序,對要傳送的資料進行編碼。
* 伺服器接收資料,要分三種情況。一種是瀏覽器直接用URL提交的資料,另外兩種是用表單的GET和POST方式提交的資料
(1)表單中POST方式提交的情況:
可以通過request.setCharacterEncoding(charset),來設定對瀏覽器提交的資料使用什麼樣的編碼進行解碼。如果不設定時,伺服器預設使用ISO-8859-1來解碼請求引數。如果頁面的contentType="GBK", 此時要想得到正確的結果,則:
Stringname = newString(request.getParameter("name").getBytes("ISO-8859-1"),"GBK");
如果設定:request.setCharacterEncoding("GBK"),則只需:String name = request .getParameter("name");
所以對於POST表單提交的資料,在獲得資料的JSP頁面中request.setCharacterEncoding要和生成提交該表單的JSP頁面的response.setCharacterEncoding設定成相同的值。還有一種方法解決POST提交亂碼問題:使用過濾器,在過濾器中設定request.setCharacterEncoding(charset)
(2)URL提交的資料和表單中GET方式提交的情況:
此時設定request.setCharacterEncoding引數是不行的,因為在Tomcat5.0中,預設情況下使用ISO-8859-1對URL提交的資料和表單中GET方式提交的資料進行解碼,而不使用該引數對URL提交的資料和表單中GET方式提交的資料解碼。要解決該問題,應該在Tomcat的配置檔案server.xml的Connector標籤中設定useBodyEncodingForURI或者URIEncoding屬性,
*useBodyEncodingForURI引數為true時表示用request.setCharacterEncoding引數對URL提交的資料和表單中GET方式提交的資料進行重新解碼。
*URIEncoding引數指定對所有GET方式請求(包括URL提交的資料和表單中GET方式提交的資料)進行統一解碼的編碼
即處理get方式請求引數有四種情況:
* 不設定server.xml的Connector標籤,此時伺服器統一對get方式的請求引數進行ISO-8859-1解碼,此時設定request.setCharacterEncoding(charset)是無效的
* 設定server.xml的Connector標籤,令useBodyEncodingForURI="true",此時使用情況和post提交方式一致
* 設定server.xml的Connector標籤,令URIEncoding="charset",此時伺服器統一對get方式的請求引數進行charset解碼
* 設定server.xml的Connector標籤,令useBodyEncodingForURI="true"URIEncoding="charset", 此時URIEncoding設定無效
<Connectorport="8080" maxThreads="150" minSpareThreads="25"maxSpareThreads="75" enableLookups="false"
redirectPort="8443"acceptCount="100" debug="0"connectionTimeout="20000" disableUploadTimeout="true"
useBodyEncodingForURI="true"URIEncoding="UTF-8"/>
範例:URLDecoder.decode(..)是伺服器端解碼的,而encodeURI(..)是js在客戶端編碼的
1. javascrip對於get方式的引數編碼: Java程式碼:url=encodeURI(url);伺服器端獲取引數後解碼: Java程式碼
Stringlinename = newString(request.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
說明:此時並沒有對server.xml的<Connector>配置useBodyEncodingForURI和URIEncoding屬性
編碼過程:瀏覽器編碼(UTF-8)--> 伺服器解碼(ISO-8859-1) --> String.getBytes方法編碼(ISO-8859-1)--> 建立String解碼(UTF-8)
2.javascript:url=encodeURI(encodeURI(url)); //用了2次encodeURI伺服器端獲取: Java程式碼
Stringlinename = request.getParameter(name); //java : 字元解碼linename =java.net.URLDecoder.decode(linename , "UTF-8");
瀏覽器編碼(UTF-8) --> 瀏覽器編碼(UTF-8)--> 伺服器解碼(ISO-8859-1) --> URLDecoder解碼(UTF-8)
3.jsp對於get方式的引數編碼:url="...."?sport=<%=URLEncoder.encoder("籃球","UTF-8") %>
伺服器端獲取引數後解碼: Java程式碼
Stringlinename = newString(request.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
說明:此時並沒有對server.xml的<Connector>配置useBodyEncodingForURI和URIEncoding屬性
編碼過程:瀏覽器編碼(UTF-8)--> 伺服器解碼(ISO-8859-1) --> String.getBytes方法編碼(ISO-8859-1)--> 建立String解碼(UTF-8)
對範例2的說明:
可能大家都覺得對中文進行兩次UTF-8編碼後,進行一次ISO-8859-1解碼和一次UTF-8解碼,為什麼得到的不是亂碼呢?
個人認為java提供的URLEncoder和URLdecoder內部是做了某些處理的,和通String.getBytes獲取位元組陣列,再newString(bytes[], charset)編解碼方式使不一樣的。如java程式碼:
String str = "漢字test";String str1 = URLEncoder.encode(str, "UTF-8");
String str2 = URLEncoder.encode(str, "ISO-8859-1");
String str3 = URLEncoder.encode(str, "UTF-8");
String str4 = URLEncoder.encode(str3, "ISO-8859-1");
//使用UTF-8編碼 解碼成功
String str6 = URLDecoder.decode(str1, "UTF-8");
//使用ISO-8859-1編碼 解碼失敗
String str7 = URLDecoder.decode(str2, "ISO-8859-1");
//編碼次數為兩次,解碼一次,解碼失敗
String str8 = URLDecoder.decode(str4, "UTF-8");
//編碼次數等於解碼次數,且最終解碼方式為字串初始編碼方式就能解碼成功,
//即使中間編碼方式為ISO-8859-1
String str9 = URLDecoder.decode(str8, "UTF-8");
從執行結果分析,個人覺得URLEncoder和URLDecoder對ASCII內的字元進行編碼, 無論URLEncoder.encode的charset引數是什麼,
得到的編碼結果都是一樣的,無論URLDecoder.decode的charset引數是什麼,得到的解碼結果都一樣,只要中文的起始編碼charset和最後解碼的chaset一致(這裡都是UTF-8),中間編碼和解碼次數對等(這裡均為兩次), 則得到的結果就不會亂碼。