HttpServletRequest獲取中文參數亂碼問題

HttpServletRequest獲取中文參數亂碼問題

​ 我們在開發的過程中,不可避免的會遇到前後端的數據通信問題,又不可避免的會遇到數據爲中文的情況,初學者在毫不知情中,就遭遇了自己的第一次中文亂碼問題,今天,我們就一起來探討下中文亂碼問題爲何會發生、以及如何解決前後端數據傳輸的中文亂碼問題。

1.問題復現

​ 爲了更好的展示中文亂碼問題,我們來舉個栗子,首先,前端頁面如下所示:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
	<form action="RequestTestServlet" method="post">
		姓名: <input type="text" name="name" style="width: 150px" />
		年齡: <input type="text" name="age" style="width: 150px" />
		<input type="submit" value="提交" />
	</form>
</body>
</html>

​ 後端Servlet中對應的獲取代碼如下:

protected void doPost(...) throws ...{
  String name = request.getParameter("name");
  String age = request.getParameter("age");
  System.out.println("姓  名: " + name);
  System.out.println("年  齡: " + age);
}

​ 頁面上輸入對應的值後,比如輸入李子樹/26,點擊提交後,後端打印的結果如下圖所示,出現了中文亂碼問題,以上爲本文背景。

資源分配圖

2.中文亂碼問題時如何出現的?

​ 其實說到中文亂碼,其出現的根本原因就是字符編碼和解碼使用的字符集不同,因爲漢字較爲複雜,以至於個人PC誕生後的一段時間,中文的輸入問題都沒有很好的解決, 可以這麼說在中文在不同的字符集中,漢字的編碼都幾乎沒有雷同。

資源分配圖

​ 就好像早期戰爭時期發電報一樣,發之前將內容按照密碼本進行編碼,轉成密碼本中對應的頁數、行數、列數,使之可以唯一的對應上一個字,接收方收到電報後,根據約定好的密碼本,再將接收到的報文進行轉碼,這樣就可以通過無線電來傳輸數據。這其中,密碼本就可以認爲是一個字符集,如果編碼和解碼使用的不是同一個密碼本,那麼就無法獲得真正的報文內容,也就是亂碼問題。

​ 上面的例子是在戰爭時期的諜報,主要是爲了加密,防止被敵人知道情報。但是到現在的信息時代,字符集的提出,是爲了統一標準,讓數據可以更好的傳輸。我們來看些常用的字符集:

  1. ASCII:編程必學必知必會,使用一個字節來表示包括拉丁字母,數字,常用標點符號及控制符,ASCII碼錶中共128個字符;
  2. ISO-8859-1:利用了ASCII剩下的128字符(1字節8位,表示範圍爲0~255),添加了對西歐語言的支持;
  3. GB2312:中國國家標準局最早發佈的簡體中文字符集,可以完美的支持中文,一個漢字佔兩個字節;
  4. GBK:在GB2312的基礎上,增加了對繁體中文的支持;
  5. UTF-8:是Unicode(萬國碼)編碼的一種實現方式,也是目前最常用的字符集,是一種變長字節編碼方式,其使用1~4個字節表示一個符號,會根據不同的符號而變化字節長度,一個漢字佔三個字節。

​ 瞭解了字符集後,我們在回過頭看下我們在第一部分遇到的問題,數據在傳輸前,因爲頁面上設置了編碼方式爲UTF-8(Content-Type),因此編碼方式爲UTF-8,而服務器收到字節流後默認使用ISO-8859-1來解碼,因此導致中文亂碼問題。

3.問題解決

​ 知道了原因,那麼問題解決起來就簡單多了,(其實在實際開發中,肉眼可見的錯誤是最好解決的,再輔以報錯信息,令人頭疼的是邏輯錯誤,尤其是多線程、高併發、環境等導致的問題)我們只需保證在瀏覽器處使用的編碼方式和服務器端一致即可。

​ 對於上述問題,我麼將服務器端的代碼修改如下:

protected void doPost(...) throws ...{
  //設置該request對象中請求體的使用的編碼方式
	request.setCharacterEncoding("utf-8");
  String name = request.getParameter("name");
  String age = request.getParameter("age");
  System.out.println("姓  名: " + name);
  System.out.println("年  齡: " + age);
}

​ 之後,我們在瀏覽器上再次點擊提交後,即可看到如下頁面,亂碼問題被解決啦。

資源分配圖

​ but、but、but,注意到上面代碼塊中的註釋了麼,setCharacterEncoding方法設置的編碼方式只對該request中的請求體有效,有我們的上篇博文中,我們說到了Http請求主要有三部分組成:請求行、請求頭、請求體,而數據包含在請求行的queryString中或者請求體的FormData中。

​ 我們在來看下setCharacterEncoding方法的方法說明,也驗證了我們剛纔的說法。

	//ServletRequest.class
	/**
     * Overrides the name of the character encoding used in the body of this
     * request. This method must be called prior to reading request parameters
     * or reading input using getReader(). Otherwise, it has no effect.
     * 
     * @param env      <code>String</code> containing the name of
     *                 the character encoding.
     * @throws         UnsupportedEncodingException if this
     *                 ServletRequest is still in a state where a
     *                 character encoding may be set, but the specified
     *                 encoding is invalid
     */
	public void setCharacterEncoding(String env) throws UnsupportedEncodingException;

​ 那我們就來驗證下以get方式,在url中傳輸數據的中文亂碼問題,我們只需將前端頁面中form表單中的method屬性由post改爲get即可,再次輸入李子樹/26,點擊提交後一起看下結果:

資源分配圖資源分配圖

​ 啥、啥、啥、啥,爲什麼會有兩張截圖,因爲你的運行結果會是這兩者之間的一種。說到這裏,我們就需要知道另一些知識了,首先我們來看張圖片:

資源分配圖

​ 這裏相信大家看到的都是相同的結果,不過這裏不是我們要說的中文亂碼發生的案發現場,在這裏我們看到的url是經過URLEncode編碼過的,瀏覽器將此次HTTP請求提交到Http服務器,敲黑板敲黑板,Http服務器在將Http請求封裝成HttpServletRequest時,對url進行了一次編碼,這也是爲啥url裏的中文參數也會出現中文亂碼問題。

​ 但是爲啥部分同學在上例中沒有出現中文亂碼問題?這裏就要說下統一編碼格式的重要性了,因爲UTF-8的強大包容性,使得其使用範圍最廣,也直接影響了Tomcat,Tomcat在8.0以後的版本中,默認編碼改爲了UTF-8,在8.0之前的版本中,默認的編碼爲ISO-8859-1。這也是爲什麼上面的程序會出現兩種結果,主要是因爲服務器的默認編碼方式的不同。

​ 那當服務器爲Tomcat V7.0時,又要如何解決url中的中文參數亂碼問題?主要有兩種方式:

  1. 在後端先講參數值使用ISO-8859-1編碼方式轉爲字節數組,再將其使用UTF-8來進行編碼,示例代碼如下:name = new String(name.getBytes("iso-8859-1"), "utf-8");
  2. 修改Tomcat服務器的默認編碼方式,保持和服務器一致,方法如下:

​ 修改tomcat安裝路徑下conf文件夾下的server.xml文件,在Connector節點上新增一屬性useBodyEncodingForURI,如下圖所示:

<Connector connectionTimeout="20000" port="8080" 
           protocol="HTTP/1.1" redirectPort="8443"
           useBodyEncodingForURI="true" />

​ 這裏需要注意的是,useBodyEncodingForURI屬性的默認值爲false,當設置爲true時,url的編碼方式則和body使用的編碼方式相同,注意,這裏在後端代碼中,需要設置body的編碼方式。

​ 同時,Tomcat還提供了一個URIEncoding屬性,用於設置所有的URL的編碼方式,修改如下圖所示:

<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080" 
           protocol="HTTP/1.1" redirectPort="8443" />

​ 當然,對於Url中的中文參數的亂碼問題(tomcat7及以前版本),推薦使用在後端先使用ISO-8859-1編碼,後使用UTF-8解碼,即方式一,這樣可以使我們的服務不依賴於服務器的配置。

4.結論

​ 對於中文亂碼問題,其本質上還是編碼和解碼方式的不同,對於一些常見的編碼方式,我們也需要做到熟悉瞭解。本文最後也介紹了兩種解決問題的方式,也希望能解決你的問題。


​ 又到了分隔線以下,本文到此就結束了,本文內容全部都是由博主自己進行整理並結合自身的理解進行總結,如果有什麼錯誤,還請批評指正。

​ Java web這一專欄會是一個系列博客,喜歡的話可以持續關注,如果本文對你有所幫助,還請還請點贊、評論加關注。

​ 有任何疑問,可以評論區留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章