深入分析java web中文編碼問題(其三)__Js

來源:互聯網
上載者:User

一、java web中涉及的編解碼

 我們都知道對於中文來說,有I/O的地方就會涉及編碼,前面已經提到了I/O操作會引起編碼,而大部分I/O引起的亂碼都是網路I/O,因為現在幾乎所有的應用程式都涉及網路操作,而資料經過網路傳輸都是以位元組為單位的,所以所有的資料都必須能夠被序列化為位元組。在java中資料要被序列化必須繼承Serializable介面。


這裡有一個問題,你是否認真考慮過一段文本它的實際大小應該怎麼計算,我曾經碰到過一個問題,就是想辦法壓縮Cookie大小,減少網路傳輸量,當時有選擇不能的壓縮演算法,發現壓縮後字元數是減少了,但是並沒有減少位元組數。所謂的壓縮只是將多個單位元組字元通過編碼轉變成一個多位元組字元,減少的是String.length(),而並沒有減少最終的位元組數。例如,整型數字1234567如果當成字元來儲存,採用UTF-8來編碼佔用7個位元組,採用UTF-16編碼會佔用14個位元組,但是把它當成int型數字來儲存只需要4個位元組來儲存,所以看一段文本的大小,看字元本身的長度是沒有意義的,即使是一樣的字元採用不同的編碼最終儲存的大小也會不同,所以從字元到位元組一定要看編碼類別型。


另外一個問題,你是否考慮過當我們在電腦中某個文字編輯器裡輸入某個漢字時,它到底是怎麼表示的。我們都知道,電腦裡所有的資訊都是0和1表示的,那麼一個漢字,他到底是多少個0和1呢。

我們能夠看到的漢字都是字元形式出現的,例如,在java中“淘寶”倆個字元在電腦中的數值十進位是28120和23453,16進位6bd8和5d9d,也就是這倆個字元是由這倆個數字唯一表示的,java中的一個char是16個bit,相當於倆個位元組,所以倆個漢字用char表示在記憶體中佔用相當於4個位元組的空間。


步入正題,下面我們來看一下java Web中哪些地方可能會存在編碼轉換。

使用者從瀏覽器端發起一個http請求,需要存在編碼的地方是URL,Cookie,Paramiter。伺服器端接收到HTTP請求後要解析HTTP協議,其中URL,Cookie和POST表單參數需要解碼,伺服器端可能還需要讀取資料庫中的資料--本地或網路中其他地方的文字檔,這些資料都可能存在編碼問題,當servlet處理完所有請求的資料後,需要將這些資料在編碼通過Socket發送到使用者請求的瀏覽器裡,再經過瀏覽器解碼成為文本。過程如圖3-1。





3.1URL的編解碼

使用者提交一個URL,這個url中可能存在中文,因此需要編碼,如何對這個URL進行編碼。根據什麼規則來編碼。又如何來解碼。圖3-2介紹URL的幾個組成部分。



以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將通過表單方式提交到伺服器端。在PathInfo和QueryString部分出現了中文,當我們在瀏覽器中直接輸入這個URL時,在瀏覽器和伺服器端會如何編碼和解析這個URL呢。大家有想過嗎。為了驗證瀏覽器是怎麼編碼url的。我們選擇google瀏覽器察看請求URL的實際內容。以下是請求結果 Request URL: https://localhost:8080/examples/servlets/servlet/%E5%8F%AF%E4%B9%90?author=%E5%8F%AF%E4%B9%90,我們發現可樂被編碼成了 %E5%8F%AF%E4%B9%90(不同瀏覽器採用的編碼不同,),為什麼會有%,因為瀏覽器編碼URL將非ASCII字元按照某種格式編碼成16進位數字後將每個16進位表示的位元組前加上%,所以最終URL就成了這個模樣。

tomcat對於URL的URI部分進行解碼的字元集是在connector的<Connector URLEncoding="UTF-8">中定義的,如果沒有定義,那麼將預設編碼——ISO-8859-1解析,所以如果又中文URL建議把URLEncoding設定為UTF-8編碼。那對於QueryString又如何解析。GET方式HTTP請求的QueryString與POST方式HTTP請求的表單參數都是作為Parameters儲存的,都通過request.getParameter擷取參數值。對它們的解碼是在request.getParameter方法第一次被調用時進行的。request.getParameter方法被調用時將會調用org.apache.catalina.connector.Request的parseParameter方法。這個方法將會對GET和POST方式傳遞的參數進行解碼,但是它們的解碼字元集有可能不一樣。對於GET請求,QueryString的解碼字元集要麼是Header中ContentType定義的Charset。要麼就是預設的IOS-8859-1。要使用ContentType中定義的編碼就要將connectorde <Connector URLEncoding="UTF-8" useBobyEncodingForURI="true"/>中的useBobyEncodingForURI設定為true,這個設定僅僅是對Qu eryString有效。

從上面的URL編碼和解碼過程來看,比較複雜,而且編碼和解碼並不是我們在應用程式中能完全控制的,所以我們的應用程式中應該盡量避免在URL中使用非ASCII字元,不然很可能會碰到亂碼問題。

3.2HTTP Header的編解碼
當用戶端發起一個HTTP請求時,除了上面的URL外還可能會在Header中傳遞其他參數,如Cookie,rediectPath等,這些使用者佈建的值很可能也會存在編碼問題,Tomcat對它們又是怎麼解碼的呢。

對Header中的項進行解碼也是在調用request.getHeader時進行的,如果請求的Header項沒有解碼則調用MessageBytes的toString的方法,這個方法將從byte到char的轉化使用的預設編碼也是IOS-8859-1,而我們也不能設定Header的其他解碼格式,所以你設定的Header中有非ASCII字元解碼肯定會亂碼。我們在添加Header時也是同樣的道理,不要在Header中傳遞非ASCII字元,如果一定要傳遞,可以先將這些字元用org.apache.catalina.util.URLEncoder編碼,然後在添加到Header中,這樣瀏覽器到伺服器的傳遞過程就不會丟失資訊了,我們要訪問這些項時再按照相應的字元集解碼就好了。

3.3POST表單的編解碼

前面提到了POST表單提交的參數的解碼是在第一次調用request.getParameter時發生的,POST表單參數傳遞方式與QueryString不同,它是通過HTTP的BODY傳遞到服務端的,當我們在頁面上單擊提交按鈕時瀏覽器首先將根據ContentType的Charset編碼格式對錶單填的參數進行編碼,然後提交到伺服器端,在伺服器端同樣也是用contentType中的字元集進行解碼的,所以通過POST表單提交的參數一般不會出現問題,而且這個字元集編碼是我們自己設定的,可以通過request.setCharacterEncoding(charset)來設定。


注意,一定要在第一次調用request.getParameter方法之前就設定request.setCharacterEncoding(charset),否則你的POST表單提交上來的資料也可能出現亂碼。


java編碼-完。

文章借鑒於 《In-depth analysis of java web insider》




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.