javaWeb 中的編碼解碼

2018-09-28 19:24 更新

javaWeb中的編碼解碼

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

編碼&解碼

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

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

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

請(qǐng)求

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

1、URL方式直接訪問。

2、頁(yè)面鏈接。

3、表單get提交

4、表單post提交

URL方式

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

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

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

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

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

browserpath部分Query String
FirefoxE6 88 91 E6 98 AFE6 88 91 E6 98 AF
ChromeE6 88 91 E6 98 AFE6 88 91 E6 98 AF
IEE6 88 91 E6 98 AFCE D2 CA C7

查閱上篇博客的編碼可知對(duì)于path部分Firefox、chrome、IE都是采用UTF-8編碼格式,對(duì)于Query String部分Firefox、chrome采用UTF-8,IE采用GBK。至于為什么會(huì)加上%,這是因?yàn)閁RL的編碼規(guī)范規(guī)定瀏覽器將ASCII字符非 ASCII 字符按照某種編碼格式編碼成 16 進(jìn)制數(shù)字然后將每個(gè) 16 進(jìn)制表示的字節(jié)前加上“%”。

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

解析請(qǐng)求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,這個(gè)方法把傳過來(lái)的 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); 
    }

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

<Connector URIEncoding="utf-8"  />

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

對(duì)于Query String部分,我們知道無(wú)論我們是通過get方式還是POST方式提交,所有的參數(shù)都是保存在Parameters,然后我們通過request.getParameter,解碼工作就是在第一次調(diào)用getParameter方法時(shí)進(jìn)行的。在getParameter方法內(nèi)部它調(diào)用org.apache.catalina.connector.Request 的 parseParameters 方法,這個(gè)方法將會(huì)對(duì)傳遞的參數(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);
                }
            }

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

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

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

表單GET

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

表單POST

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

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)