鍍金池/ 教程/ Java/ javaWeb中的編碼解碼
編碼詳情
java編碼轉(zhuǎn)換過程
認(rèn)識字符集
javaWeb中的編碼解碼
認(rèn)識字符集
總結(jié)
java是如何編碼解碼的
解決URL中文亂碼問題
JSP頁面編碼過程

javaWeb中的編碼解碼

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

編碼&解碼

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

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/6.1.png" alt="" />

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

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

請求

客戶端想服務(wù)器發(fā)送請求無非就通過四中情況:

1、URL方式直接訪問。

2、頁面鏈接。

3、表單get提交

4、表單post提交

URL方式

對于URL,如果該URL中全部都是英文的那倒是沒有什么問題,如果有中文就要涉及到編碼了。如何編碼?根據(jù)什么規(guī)則來編碼?又如何來解碼呢?下面LZ將一一解答!首先看URL的組成部分:

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/6.2.png" alt="" />

在這URL中瀏覽器將會對path和parameter進(jìn)行編碼操作。為了更好地解釋編碼過程,使用如下URL

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

將以上地址輸入到瀏覽器URL輸入框中,通過查看http 報文頭信息我們可以看到瀏覽器是如何進(jìn)行編碼的。下面是IE、Firefox、Chrome三個瀏覽器的編碼情況:

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/6.3.png" alt="" />

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/6.4.png" alt="" />

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/6.5.png" alt="" />

可以看到各大瀏覽器對“我是”的編碼情況如下:

browser 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的編碼規(guī)范規(guī)定瀏覽器將ASCII字符非 ASCII 字符按照某種編碼格式編碼成 16 進(jìn)制數(shù)字然后將每個 16 進(jìn)制表示的字節(jié)前加上“%”。

當(dāng)然對于不同的瀏覽器,相同瀏覽器不同版本,不同的操作系統(tǒng)等環(huán)境都會導(dǎo)致編碼結(jié)果不同,上表某一種情況,對于URL編碼規(guī)則下任何結(jié)論都是過早的。由于各大瀏覽器、各個操作系統(tǒng)對URL的URI、QueryString編碼都可能存在不同,這樣對服務(wù)器的解碼勢必會造成很大的困擾,下面我們將已tomcat,看tomcat是如何對URL進(jìn)行解碼操作的。

解析請求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,這個方法把傳過來的 URL 的 byte[] 設(shè)置到 org.apache.coyote.Request 的相應(yīng)的屬性中。這里的 URL 仍然是 byte 格式,轉(zhuǎn)成 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"  />

如果沒有定義則會采用默認(rèn)編碼ISO-8859-1來解析。

對于Query String部分,我們知道無論我們是通過get方式還是POST方式提交,所有的參數(shù)都是保存在Parameters,然后我們通過request.getParameter,解碼工作就是在第一次調(diào)用getParameter方法時進(jìn)行的。在getParameter方法內(nèi)部它調(diào)用org.apache.catalina.connector.Request 的 parseParameters 方法,這個方法將會對傳遞的參數(shù)進(jìn)行解碼。下面代碼只是parseParameters方法的一部分:

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

從上面代碼可以看出對query String的解碼格式要么采用設(shè)置的ChartSet要么采用默認(rèn)的解碼格式ISO-8859-1。注意這個設(shè)置的ChartSet是在 http Header中定義的ContentType,同時如果我們需要改指定屬性生效,還需要進(jìn)行如下配置:

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

上面部分詳細(xì)介紹了URL方式請求的編碼解碼過程。其實對于我們而言,我們更多的方式是通過表單的形式來提交。

表單GET

我們知道通過URL方式提交數(shù)據(jù)是很容易產(chǎn)生亂碼問題的,所以我們更加傾向于通過表單形式。當(dāng)用戶點(diǎn)擊submit提交表單時,瀏覽器會更加設(shè)定的編碼來編碼數(shù)據(jù)傳遞給服務(wù)器。通過GET方式提交的數(shù)據(jù)都是拼接在URL后面(可以當(dāng)做query String??)來提交的,所以tomcat服務(wù)器在進(jìn)行解碼過程中URIEncoding就起到作用了。tomcat服務(wù)器會根據(jù)設(shè)置的URIEncoding來進(jìn)行解碼,如果沒有設(shè)置則會使用默認(rèn)的ISO-8859-1來解碼。假如我們在頁面將編碼設(shè)置為UTF-8,而URIEncoding設(shè)置的不是或者沒有設(shè)置,那么服務(wù)器進(jìn)行解碼時就會產(chǎn)生亂碼。這個時候我們一般可以通過new String(request.getParameter(“name”).getBytes(“iso-8859-1″),”utf-8″) 的形式來獲取正確數(shù)據(jù)。

表單POST

對于POST方式,它采用的編碼也是由頁面來決定的即contentType。當(dāng)我通過點(diǎn)擊頁面的submit按鈕來提交表單時,瀏覽器首先會根據(jù)ontentType的charset編碼格式來對POST表單的參數(shù)進(jìn)行編碼然后提交給服務(wù)器,在服務(wù)器端同樣也是用contentType中設(shè)置的字符集來進(jìn)行解碼(這里與get方式就不同了),這就是通過POST表單提交的參數(shù)一般而言都不會出現(xiàn)亂碼問題。當(dāng)然這個字符集編碼我們是可以自己設(shè)定的:request.setCharacterEncoding(charset) 。

上一篇:編碼詳情