java中文亂碼解決之道(六)-----javaWeb中的編碼解碼

在上篇博客中LZ介紹了前面兩種場景(IO、內存)中的java編碼解碼操做,其實在這兩種場景中咱們只須要在編碼解碼過程當中設置正確的編碼解碼方式通常而言是不會出現亂碼的。對於咱們從事java開發的人而言,其實最容易也是產生亂碼最多的地方就是web部分。首先咱們來看在javaWeb中有哪些地方存在編碼轉換操做。java

編碼&解碼

經過下圖咱們能夠了解在javaWeb中有哪些地方有轉碼:web

201501060001

用戶想服務器發送一個HTTP請求,須要編碼的地方有url、cookie、parameter,通過編碼後服務器接受HTTP請求,解析HTTP請求,而後對url、cookie、parameter進行解碼。在服務器進行業務邏輯處理過程當中可能須要讀取數據庫、本地文件或者網絡中的其餘文件等等,這些過程都須要進行編碼解碼。當處理完成後,服務器將數據進行編碼後發送給客戶端,瀏覽器通過解碼後顯示給用戶。在這個整個過程當中涉及的編碼解碼的地方較多,其中最容易出現亂碼的位置就在於服務器與客戶端進行交互的過程。chrome

上面整個過程能夠歸納成這樣,頁面編碼數據傳遞給服務器,服務器對得到的數據進行解碼操做,通過一番業務邏輯處理後將最終結果編碼處理後傳遞給客戶端,客戶端解碼展現給用戶。因此下面我就請求對javaweb的編碼&解碼進行闡述。數據庫

請求

客戶端想服務器發送請求無非就經過四中狀況:apache

一、URL方式直接訪問。瀏覽器

二、頁面連接。tomcat

三、表單get提交服務器

四、表單post提交cookie

URL方式

對於URL,若是該URL中所有都是英文的那卻是沒有什麼問題,若是有中文就要涉及到編碼了。如何編碼?根據什麼規則來編碼?又如何來解碼呢?下面LZ將一一解答!首先看URL的組成部分:網絡

201501060002

在這URL中瀏覽器將會對path和parameter進行編碼操做。爲了更好地解釋編碼過程,使用以下URL

http://127.0.0.1:8080/perbank/我是cm?name=我是cm

將以上地址輸入到瀏覽器URL輸入框中,經過查看http 報文頭信息咱們能夠看到瀏覽器是如何進行編碼的。下面是IE、Firefox、Chrome三個瀏覽器的編碼狀況:

201501080001

201501080002

201501080003

能夠看到各大瀏覽器對「我是」的編碼狀況以下:

 

path部分

Query String

Firefox

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

Chrome

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

IE

E6 88 91 E6 98 AF

CE D2 CA C7

查閱上篇博客的編碼可知對於path部分Firefox、chrome、IE都是採用UTF-8編碼格式,對於Query String部分Firefox、chrome採用UTF-8,IE採用GBK。至於爲何會加上%,這是由於URL的編碼規範規定瀏覽器將ASCII字符非 ASCII 字符按照某種編碼格式編碼成 16 進制數字而後將每一個 16 進製表示的字節前加上「%」。

固然對於不一樣的瀏覽器,相同瀏覽器不一樣版本,不一樣的操做系統等環境都會致使編碼結果不一樣,上表某一種狀況,對於URL編碼規則下任何結論都是過早的。因爲各大瀏覽器、各個操做系統對URL的URI、QueryString編碼均可能存在不一樣,這樣對服務器的解碼勢必會形成很大的困擾,下面咱們將已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 方法中完成的:

protected void convertURI(MessageBytes uri, Request request) 
             throws Exception { 
                    ByteChunk bc = uri.getByteChunk(); 
                    int length = bc.getLength(); 
                    CharChunk cc = uri.getCharChunk(); 
                    cc.allocate(length, -1); 
                    String enc = connector.getURIEncoding();     //獲取URI解碼集
                    if (enc != null) { 
                        B2CConverter conv = request.getURIConverter(); 
                        try { 
                            if (conv == null) { 
                                conv = new B2CConverter(enc); 
                                request.setURIConverter(conv); 
                            } 
                        } catch (IOException e) {...} 
                        if (conv != null) { 
                            try { 
                                conv.convert(bc, cc, cc.getBuffer().length - cc.getEnd()); 
                                uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength()); 
                                return; 
                            } catch (IOException e) {...} 
                        } 
                    } 
                    // Default encoding: fast conversion 
                    byte[] bbuf = bc.getBuffer(); 
                    char[] cbuf = cc.getBuffer(); 
                    int start = bc.getStart(); 
                    for (int i = 0; i < length; i++) { 
                        cbuf[i] = (char) (bbuf[i + start] & 0xff); 
                    } 
                    uri.setChars(cbuf, 0, length); 
    }

從上面的代碼可知,對URI的解碼操做是首先獲取Connector的解碼集,該配置在server.xml中

<Connector URIEncoding="utf-8"  />

若是沒有定義則會採用默認編碼ISO-8859-1來解析。

對於Query String部分,咱們知道不管咱們是經過get方式仍是POST方式提交,全部的參數都是保存在Parameters,而後咱們經過request.getParameter,解碼工做就是在第一次調用getParameter方法時進行的。在getParameter方法內部它調用org.apache.catalina.connector.Request 的 parseParameters 方法,這個方法將會對傳遞的參數進行解碼。下面代碼只是parseParameters方法的一部分:

          //獲取編碼
             String enc = getCharacterEncoding();
            //獲取ContentType 中定義的 Charset
            boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
            if (enc != null) {    //若是設置編碼不爲空,則設置編碼爲enc
                parameters.setEncoding(enc);
                if (useBodyEncodingForURI) {   //若是設置了Chartset,則設置queryString的解碼爲ChartSet
                    parameters.setQueryStringEncoding(enc);    
                }
            } else {     //設置默認解碼方式
                parameters.setEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                }
            }

從上面代碼能夠看出對query String的解碼格式要麼採用設置的ChartSet要麼採用默認的解碼格式ISO-8859-1。注意這個設置的ChartSet是在 http Header中定義的ContentType,同時若是咱們須要改指定屬性生效,還須要進行以下配置:

<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/>

上面部分詳細介紹了URL方式請求的編碼解碼過程。其實對於咱們而言,咱們更多的方式是經過表單的形式來提交。

表單GET

咱們知道經過URL方式提交數據是很容易產生亂碼問題的,因此咱們更加傾向於經過表單形式。當用戶點擊submit提交表單時,瀏覽器會更加設定的編碼來編碼數據傳遞給服務器。經過GET方式提交的數據都是拼接在URL後面(能夠當作query String??)來提交的,因此tomcat服務器在進行解碼過程當中URIEncoding就起到做用了。tomcat服務器會根據設置的URIEncoding來進行解碼,若是沒有設置則會使用默認的ISO-8859-1來解碼。假如咱們在頁面將編碼設置爲UTF-8,而URIEncoding設置的不是或者沒有設置,那麼服務器進行解碼時就會產生亂碼。這個時候咱們通常能夠經過new String(request.getParameter("name").getBytes("iso-8859-1"),"utf-8") 的形式來獲取正確數據。

表單POST

對於POST方式,它採用的編碼也是由頁面來決定的即contentType。當我經過點擊頁面的submit按鈕來提交表單時,瀏覽器首先會根據ontentType的charset編碼格式來對POST表單的參數進行編碼而後提交給服務器,在服務器端一樣也是用contentType中設置的字符集來進行解碼(這裏與get方式就不一樣了),這就是經過POST表單提交的參數通常而言都不會出現亂碼問題。固然這個字符集編碼咱們是能夠本身設定的:request.setCharacterEncoding(charset) 。


-----原文出自:http://cmsblogs.com/?p=1510,請尊重做者辛勤勞動成果,轉載說明出處.

-----我的站點:http://cmsblogs.com

相關文章
相關標籤/搜索