寫(xiě)了N多項目后,突然發(fā)現最近又碰到了中文編碼不正確的問(wèn)題,這在剛入java行時(shí)才會(huì )有的問(wèn)題,怎么會(huì )出現在我這個(gè)熟手身上,有點(diǎn)不可思議。當然,先憑經(jīng)驗按常規來(lái)解決啦,該設置的地方都設置了,最終還是沒(méi)法解決這個(gè)問(wèn)題。我的應用是在一個(gè)struts2的action中要重定向到一個(gè)外部url,而這個(gè)url的參數中帶有漢字,漢字作utf-8編碼,當一跳轉后,在外部url的servlet處接收參數,查看參數就是亂碼了。
常規解決處理方法:
a.把所有的jsp頁(yè)面的charset設置為UTF-8。
b.添加過(guò)濾器,在filter內調用request.setCharacterEncoding("utf-8")方法將request的字符集設定為utf-8。
c.Tomcat(或jboss)的URIEncoding默認是ISO-8859-1,需要設置為UTF-8。
以上的方法全使上,就能解決我上面的應用了。不過(guò),在調試過(guò)程中,曾直接使用瀏覽器直接輸入帶漢字的url,還是無(wú)法解決亂碼的問(wèn)題。因此,上面的方法也僅能解決提交的部分場(chǎng)景的亂碼問(wèn)題。欲知如何,請看下節分解。
在實(shí)際的應用當中,主要會(huì )有以下四種使用方法:
1、表單的get提交
2、表單的post提交
3、頁(yè)面鏈接傳遞中文參數
4、地址欄中參數直接輸入中文提交(直接在ie或firefox中敲上中文進(jìn)行訪(fǎng)問(wèn))
在分析解決以上的幾種情況前,首先我們先了解一下,一個(gè)url請求并響應的流程
瀏覽器 IE/FireFox ----------->Servlet容器------------------------>顯示頁(yè)面
編碼 使用容器的URIEncoding轉碼 根據頁(yè)面的編碼設置進(jìn)行解碼
1.表單get方式提交
瀏覽器根據頁(yè)面的charset編碼方式對頁(yè)面進(jìn)行編碼,然后提交至服務(wù)器。首先,進(jìn)入對應的字符編碼過(guò)濾器(如果有的話(huà)),不過(guò),Tomcat6.0,對于 get提交方式采用的是server.xml文件中的URIEncoding編碼方式,而并不會(huì )采用過(guò)濾器中設置的編碼,那么,根據我的環(huán)境設置,jsp頁(yè)面都使用UTF-8的編碼,Servlet容器的URIEncoding也設置為UTF-8,則servlet不用進(jìn)行轉碼即可正確解碼,獲得正常的中文 字符串。那么,響應頁(yè)面的中文因為頁(yè)面的統一編碼(UTF-8)自然也會(huì )正常顯示。當然,如果我們的Tomcat的URIEncoding設置為其他,非UTF-8的編碼方式時(shí),頁(yè)面的內容進(jìn)入Tomcat解析時(shí),因為T(mén)omcat和頁(yè)面的編碼不統一,就需要轉碼。
例如,如果我們采用Tomcat默認的 ISO-8859-1,那么當我們使用request.getParameter("param1")獲取表單參數值時(shí),其實(shí)Servlet就進(jìn)行了轉碼,轉碼過(guò)程為UTF-8-->ISO-58859-1(我的頁(yè)面charset都是UTF-8),java偽代碼如下: new String(param1.getBytes("UTF-8"), "ISO-8859-1");
new String(變量值.getBytes("UTF-8"),"ISO-8859-1");
例如表單的username屬性以字符串"編輯"提交,那么進(jìn)入容器后,FormBean中的這個(gè)變量會(huì )亂碼,request.getParameter(username)一樣的效果,s1就是request返回的結果,下面是內存快照。
不過(guò),即使是這樣,我們依然可以使用非常規的方法,取出并顯示出正常的中文,即逆向轉碼,例如上面的亂碼,我們可以通過(guò)ISO8859-1-->UTF-8轉換一次,還原出正確的中文。
綜上所述,將Tomcat的URIEncoding設置為UTF-8(即和頁(yè)面的編碼一致即可)就能解決這類(lèi)情況,get時(shí),頁(yè)面會(huì )先按頁(yè)面設置的編碼編碼,再提交至web server,顯示時(shí)會(huì )再根據顯示頁(yè)面的編碼進(jìn)行解碼。
2.表單的post提交
對于這種方式的請求,request.setCharacterEncoding(一般來(lái)自于web.xml中過(guò)濾器設置的參數)方法進(jìn)行編碼,設置將會(huì )產(chǎn)生作用,struts的表單提交方式默認為post方式,因此,如果都采用UTF-8編碼方式,就不會(huì )產(chǎn)生中文亂碼問(wèn)題。
3.頁(yè)面鏈接中傳遞中文參數
我虛擬一個(gè)這樣的場(chǎng)景,請求頁(yè)面中有如下代碼
Html代碼
<%
String username = "編輯";
%>
<a href="hello.do?username=<%=username%>">頁(yè)面中鏈接傳遞中文</a>
對于這種方式,我們需要先將參數使用統一的編碼方式編碼,將編碼后的字符放入鏈接,這里我對參數以UTF-8方式編碼,如下
Java代碼
<%
String username = java.net.URLEncoder.encode("編輯","UTF-8");
%>
那么,這樣我們也不會(huì )產(chǎn)生中文亂碼問(wèn)題,因為,字符串編碼的處理過(guò)程:字符串->UTF-8(提交的頁(yè)面charset)->UTF-8(web server URIE)->UTF-8(顯示的頁(yè)面)。
4.地址欄中參數直接輸入中文提交
考慮以下場(chǎng)景,在瀏覽器地址欄中直接輸入"http://localhost:8080/helloapp.do?username=編輯"提交,對于這種方式,瀏覽器不會(huì )采用頁(yè)面的charset方式,也不會(huì )按照f(shuō)ilter設置的編碼方式。URL中的中文進(jìn)行編碼后,提交至服務(wù)器(IE,FireFox都一樣),而是采用系統的GBK(估計可能和browser的語(yǔ)言版本或設置相關(guān),我的機器上是編碼到GBK)轉碼為ISO- 8859-1之后,提交至Servlet容器,那么,如果對于前三種方式我們所做的設置,此種場(chǎng)景下就不正常了。因為,進(jìn)入容器時(shí)中文進(jìn)行了GBK至ISO- 8859-1的轉碼,而之前我們的Servlet容器URIEncoding設置為UTF-8,當我們使用 request.getParameter("username")時(shí),相當于又進(jìn)行了這樣的流程GBK-->ISO- 8859-1-->UTF-8,按照以上我們使用的測試,那么就會(huì )是亂碼了。此時(shí),如果是使用GBK-->ISO- 8859-1-->GBK的方式轉換,那么就能正常取出中文漢字。
對于這種情況,我們可以采用的解決辦法就是,Tomcat的URIEncoding采用默認的ISO-8859-1字符集,那么我們可以在程序中通過(guò)ISO-8859-1-->GBK這樣的轉碼方式得到正常的中文“編輯”,但這樣的結果是,我們get請求方式的中文處理解決辦法,就有問(wèn)題了。
綜上分析所述,對于亂碼問(wèn)題,前三種方式是一般用戶(hù)的請求方式,第四種屬于非正常途徑的請求方式,對于這種方式產(chǎn)生的問(wèn)題,可能無(wú)法很好的解決(和瀏覽器有關(guān)server端無(wú)法控制)。測試IE6的設置會(huì )影響應用路徑的編碼方式,例如地址欄中請求一個(gè)中文JSP頁(yè)面,如:http://localhost:8080/helloapp/編輯.jsp,IE默認是勾選"以UTF-8發(fā)送 URL"項的,那么按照我上面總結的處理方式,這個(gè)請求可以正常顯示頁(yè)面,如圖:
如果取消IE的這個(gè)選項,那么瀏覽器會(huì )以GBK編碼應用路徑的中文,得到的結果如圖:
按照我上面的設置,如果將Tomcat的URIEncoding設置為GBK,則也可以正常顯示頁(yè)面。對于FireFox3.0,則是以UTF-8編碼。因此,第四種場(chǎng)景是和客戶(hù)端相關(guān)。
因此,在項目中盡量避免第4種場(chǎng)景的情況出現,就基本可以解決java web開(kāi)發(fā)中的亂碼問(wèn)題了。