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


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

編碼&解碼

通過下圖我們可以了解在javaWeb中有哪些地方有轉碼:

201501060001

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

上面整個過程可以概括成這樣,頁面編碼數據傳遞給服務器,服務器對獲得的數據進行解碼操作,經過一番業務邏輯處理后將最終結果編碼處理后傳遞給客戶端,客戶端解碼展示給用戶。所以下面我就請求對javaweb的編碼&解碼進行闡述。

請求

客戶端想服務器發送請求無非就通過四中情況:

1、URL方式直接訪問。

2、頁面鏈接。

3、表單get提交

4、表單post提交

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM