假設現在有個form表單,當頁面中提交一個包含中文的請求時,在服務端有可能出現中文亂碼問題。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <form action="registerServlet" method="POST"> 姓名:<input type="text" name="name"><br> 年齡:<input type="text" name="age"><br> <input type="submit" value="注冊"> </form> </body> </html>
亂碼的產生原因
Http協議中規定,數據的傳輸采用字節編碼方式,即無論瀏覽器提交的數據所包含的中文是什么字符編碼格式,一旦由瀏覽器經過Http 協議傳輸,則這些數據均將以 字節的形式上傳給服務器。 因為 HTTP 協議的底層使用的是 TCP 傳輸協議。 TCP (Transmission Control Protocol) 傳輸控制協議 ,是一種面向連接的、可靠的、基於字節流的、端對端的通信協議。在請求中,這些字節均以 開頭,並以十六進制形式出現。如 %5A%3D 等。
當用戶通過瀏覽器提交一個包含 UTF 8 編碼格式的兩個字的中文請求時,瀏覽器會將這兩個中文字符變為六個字節(一般一個 UTF 8 漢字占用三個字節),即形成六個類似 %8E 的字節表 示形式,並將這六個字節上傳至 Tomcat 服務器。Tomcat 服務器在接收到這六個字節后,並不知道它們原始采用的是什么字符編碼。而Tomcat 默認的編碼格式為 ISO 8859-1 。所以會將這六個字節按照 ISO 8859-1 的格式進行編碼,這種編碼方式不支持中文,這樣就出現了亂碼。
關於請求的亂碼的解決方案
一、針對POST提交方式的解決方案
<!--
設置了請求正文的字符編碼,服務器解碼的時候會按照UTF-8解碼
但是這種方式對於GET方式的請求不適用,因為只對請求正文有作用,
GET方式的請求正文為空行,請求的參數出現在請求行 -->
request.setCharacterEncoding("UTF-8");
二、針對GET提交方式的解決方案
對於GET方式提交的情況,上述中設置request.setCharacterEncoding("UTF-8");是不會起作用的,因為GET方式的請求參數是在請求行
目前了解的兩種解決方案:
①Tomcat的版本影響,我自己試了一下Tomcat的7.*版本的,什么都不設置的話,會有亂碼,但是用7.*以上的Tomcat服務器8.*,9.*的版本就不會出現亂碼
②當我們以GET方式向服務器提交數據的話,在地址欄中,URL中URI部分的參數提交的中文會以字節的方式顯示,對於請求路徑中所攜帶參數的解析,由 Tomcat 服務器完成。而 Tomcat 服務器的字符編碼默認為 ISO8859-1 ,所以會將請求路徑中所攜帶的數據,按照 ISO8859 1 進行編碼。
這種情況下我們可以通過修改 Tomcat 默認字符編碼的方式來解決 GET 提交方式中攜帶中文的亂碼問題。在 Tomcat 安裝目錄的 conf/server.xml 中,找到端口號為 8080 的 <Connector>標簽,在其中添加 URIEncoding=UTF-8 的設置,即可將 Tomcat 默認字符編碼修改為 UTF-8 。
(不建議,如果服務器有多個虛擬服務器,修改后會重啟多個應用程序)
三、萬能的解決方案(比較通用但是還是有弊端)
String name = request.getParameter("name");
byte[] bytes = name.getBytes("ISO8859-1");
name = new String(bytes, "UTF-8");
該方式無需設置Tomcat 中的 server.xml 中的 Tomcat 默認字符編碼,無需設置 request的請求體的字符編碼。從數據在請求中的存放形式,到數據被Tomcat 中的 Servlet 接收到后的存放形式,均是由單個字 節的形式存在,而在眾多字符編碼格式中, ISO8859-1 為單字節編碼,所以,首先以 ISO8859 -1 的形式先對單字節的數據進行編碼,並將編碼后的數據存放在字節數組中。然后,再將字節數組中的數據,按照指定的 UTF -8 格式進行解碼,即變為了需要的 UTF- 8 字符編碼的數據,解決了中文亂碼問題。
編碼,即重新編排,即打散,將字符串 打散后按照指定編碼進行重新編排。 這里的編碼使用的是 String 的 getBytes() 方法,完成的工作是:按照當前字符編碼將數據打散。
解碼,即解釋執行,即組裝,對打散的字符按照指定編碼組裝后進行解釋執行。 這里的解碼使用的是StringString的帶參構造器,完成的工作是:按照原有字符編碼將數據組裝。的帶參構造器,完成的工作是:按照原有字符編碼將數據組裝。
該方式針對POST與與GET提交方式,均起作用。
響應的亂碼問題
產生原因:之所以響應時會產生亂碼,是因為 HTTP 協議中規定,默認響應體的字符編碼為ISO-8859-1 。所以,若要解決亂碼問題,就需要修改響應體的默認編碼。
解決方案:
①
response.setContentType("text/html“);//設置MIME類型
response.setCharacterEncoding("UTF-8");
修改 MIME 的字符編碼,即修改響應體的字符編碼。但使用 setCharacterEncoding() 方法的前提是,之前必須要通過使用方法 setContentType() 方法設置響應內容的 MIME 類型。否則 setCharacterEncoding() 方法不起作用。
②
response.setContentType("text/html;charset=UTF-8");
用於 設置響應 內容 的 MIME 類型 ,其中可以指定 MIME 的字符編碼。而 MIME 的字符編碼,即響應體的字符編碼 。
①②是一樣的,沒啥區別!!!
瀏覽器會根據響應體字符編碼,自動調整其對響應體內容的解碼方式,即會使用響應體的字符編碼顯示響應體內容。不過,需要注意一點,這些設置,必須在PrintWriter 對象產生之前先設置,否則將不起作用。
重定向的亂碼解決方式
//底層按字節流傳輸,打散 name = URLEncoder.encode(name, "UTF-8"); //另一邊接收 //組裝 name = URLDecoder.decode(name, "UTF-8");
byte[] bytes = name.getBytes("ISO8859-1");
name = new String(bytes, "UTF-8");