Java Web中的編碼問題(一)

Java Web中的編碼問題(一)前端

注:部份內容來源於網絡java

1.爲何須要編碼

       計算機的底層只認識010101…,這是計算認識的語言。而人類使用語言有漢語、英語、日語等等。人類的語言計算機是不認識的。因此計算機和人若是想交互的話,就須要進行翻譯,預約一種規則,例如:一個漢字「遠」在計算機中用幾個01表示。web

       而這個規則就是字符集,一個字符集會提供字符到01之間的映射關係。以下圖數據庫

2.java中須要編碼的場景

       首先是在I/O中存在的編碼,通常是在字符和字節之間的轉換。通常在I/O操做時使用的編解碼字符集一致的話就不會出現亂碼問題。apache

       其次是在內存中存在的編碼。String提供了構造方法將byte[]按指定的字符集解碼成字符串,一級getByte()按自定字符集進行編碼。瀏覽器

 

3.UTF-8

       UTF-8算是如今用的比較多的字符集,它對漢字採用3個字節表示。而且UTF-8能對單個字符的編值進行校驗,例如一個utf-8編碼的byte[]中若是其中的一個字符被損壞,不會影響其餘字符的碼值。因此,utf-8更適合網絡傳輸。tomcat

 

4.在java web中設計的編解碼

       從使用中文的角度來將,有I/O的地方就會設計編碼。服務器

       數據通過網絡傳輸都是以字節爲單位的,因此全部的數據都是可以被序列化爲字節的。在java中數據要被序列化,比繼續實現Serializable接口。網絡

 

       用戶從瀏覽器發起一個HTTP請求,須要存在編碼的地方是URL、Cookie、Parameter。編碼

服務端接收到HTTP請求後要解析HTTP,其中URL、Cookie和POST表單參數須要解碼,服務器可能須要讀取數據庫中的數據、本地或網絡中其餘地方的文本文件,這些數據均可能存在編碼問題。當Servlet處理完全部的請求的數據後,須要將這些數據再編碼,經過Socket發送到用戶請求的瀏覽器裏,在通過瀏覽器解碼稱爲文本。

 

4.1 URL的編解碼

       用戶提交一個URL,在這個URL中可能存在中文,所以須要編碼。下圖介紹了URL(這裏所說的URL和URL是針對Servlet進行描述的,也就是request.getRequestURL()和request.getRequestURI()返回的URL和URI進行描述的)的幾個組成部分。

       以tomcat做爲Servlet Engine爲例,把他們分別對應到下面的這些配置文件中。

       Port對應在tomcat的<Connector port=」8080」/>中配置,而Context Path在<Context path=」/examples」/>中配置,Servlet Path在Web應用中的web.xml的<url-pattern>中配置,PathInfo是咱們請求的具體的Servlet,QueryString是要傳遞的參數。注意這裏是在瀏覽器直接輸入URL,因此是以GET方法請求的,若是經過POST方法請求,QueryString將經過表單方式提交到服務器端。

       上圖中的ServletPath和QueryString中部分出現了中文,當咱們在瀏覽器中直接輸入這個URL時,在瀏覽器和服務器端時如何編碼和解析這個URL呢?

       咱們經過谷歌瀏覽器調試觀察咱們請求的URL的實際內容。

       長遠的編碼結果爲E995BF 和 E8BF9C,可知PathInfo是utf-8編碼,QueryString也是utf-8編碼。置於爲何會有「%」,查閱URL的編碼規範RFC3986可知,瀏覽器編碼URL是將非ASCII字符按照某種編碼格式編碼成16禁止數字後將每一個16進製表示的字節前加上「%」,因此最終的URL就成上面的格式。

       有的瀏覽器對PathInfo和QueryString的編碼是不同的,不一樣的瀏覽器對PathInfo的編碼也可能不同。

       以tomcat爲例看一下,Tomcat接收到這個URL是如何解碼的。

       解析請求的URL是在org.apache.coyote.http11.InternalInputBuffer的parseRequestLine方法中進行的,這個方法把傳過來的URL的byte[]設置到org.apache.coyote.Request的相應屬性中。這裏的URL仍然是byte格式,轉成char是在org.apache.catalina.connector.CoyoteAdapter的convertURI方法中。

對URL的URI部分進行解碼的字符集是在connector的<Connector URIEncoding=」UTF-8」>中定義的,若是沒有定義,那麼將以默認編碼ISO-8859-1解析。因此有中文URL是最好把URIEncoding設置成UTF-8編碼。

 

對於QueryString的解析過程:

以GET方式HTTP請求的QueryString與以POST方式的HTTP請求的表單參數都是做爲Parameters保存的,都經過request.getParameter獲取參數值。對它們的解碼是在request.getParameter方法第一次調用時進行的

    request.getParameter方法被調用時將會調用org.apache.catalina.connector.Request的parseParameters方法。這個方法將會對GET和POST方式傳遞的參數進行解碼。可是它們的解碼字符集有可能不同。QueryString的解碼字符集是在哪裏定義的呢。它自己是經過HTTP的Header傳到服務端的,而且也在URL中。

    QueryString的解碼字符集要麼是Header中ContentType定義的Charset,要麼是ISO-8859-1,要使用ContentType中定義的編碼,就要將connector的<Connector URIEncoding=」UTF-8」 useBodyEncodingForURI=」true」/>中的useBodyEncodingForURI設置爲true。這個項的名字容易讓人產生混淆,它並非對整個URI都採用BodyEncoding進行解碼,而僅僅是對QueryString使用BodyEncoding解碼,這一點還要特別注意。

 

    在咱們的應用程序中,應該儘可能避免在URL中使用非ASCII字符,否則極可能會碰到亂碼問題。固然咱們在服務端最好設置<Connecter/>中的URIEncoding和useBodyEncodingForURI連個參數。

4.2一種不正常的正確編碼

咱們在前端經過get請求提交了一個參數包含中文,例如:username=長遠,在服務器這邊經過

request.getParameter獲取時,拿到

       ????

而後咱們經過

username = new String(username.getBytes(「ISO-8859-1」), 「UTF-8」);

就能拿到正確的中文。

       這其中起始發生兩次編解碼(以谷歌瀏覽器爲例):

(1)   第一次編碼,瀏覽器將「長遠」編碼成爲byte[];

(2)   第一次解碼,Tomcat這邊接收到這個byte[],沒有因爲沒有設置QueryString的的字符集,以默認字符集ISO-8859-1進行解碼,也就是把這個byte[]當成ISO-8859-1格式進行解碼。也就是request.getParameter獲取到的結果????;

(3)   第二次編碼,username.getBytes(「ISO-8859-1」)發生了第二次編碼,此時返回的byte[]和瀏覽器發過來的是同樣的,utf-8編碼的;

(4)   第二次解碼,new String(username.getBytes(「ISO-8859-1」), 「UTF-8」),咱們經過(3)拿到了實際編碼格式utf-8的byte[],再用utf-8進行解碼,固然就獲得了正確的結果。

 

這其中多了一次編解碼,如在實際生產環境中,若是服務器沒有使用正確的字符集去解析前端傳過來的中文,經過這種方式進行轉碼,無疑增長的服務器的負擔。

相關文章
相關標籤/搜索