java中文亂碼解決之道(六)-----javaWeb中的編碼解碼,java-----javaweb
在上篇部落格中LZ介紹了前面兩種情境(IO、記憶體)中的java編碼解碼操作,其實在這兩種情境中我們只需要在編碼解碼過程中設定正確的編碼解碼方式一般而言是不會出現亂碼的。對於我們從事java開發的人而言,其實最容易也是產生亂碼最多的地方就是web部分。首先我們來看在javaWeb中有哪些地方存在編碼轉換操作。
編碼&解碼
通過我們可以瞭解在javaWeb中有哪些地方有轉碼:
使用者想伺服器發送一個HTTP請求,需要編碼的地方有url、cookie、parameter,經過編碼後伺服器接受HTTP請求,解析HTTP請求,然後對url、cookie、parameter進行解碼。在伺服器進行商務邏輯處理過程中可能需要讀取資料庫、本地檔案或者網路中的其他檔案等等,這些過程都需要進行編碼解碼。當處理完成後,伺服器將資料進行編碼後發送給用戶端,瀏覽器經過解碼後顯示給使用者。在這個整個過程中涉及的編碼解碼的地方較多,其中最容易出現亂碼的位置就在於伺服器與用戶端進行互動的過程。
上面整個過程可以概括成這樣,頁面編碼資料傳遞給伺服器,伺服器對獲得的資料進行解碼操作,經過一番商務邏輯處理後將最終結果編碼處理後傳遞給用戶端,用戶端解碼展示給使用者。所以下面我就請求對javaweb的編碼&解碼進行闡述。
請求
用戶端想伺服器發送請求無非就通過四中情況:
1、URL方式直接存取。
2、頁面連結。
3、表單get提交
4、表單post提交
URL方式
對於URL,如果該URL中全部都是英文的那倒是沒有什麼問題,如果有中文就要涉及到編碼了。如何編碼?根據什麼規則來編碼?又如何來解碼呢?下面LZ將一一解答!首先看URL的組成部分:
在這URL中瀏覽器將會對path和parameter進行編碼操作。為了更好地解釋編碼過程,使用如下URL
http://127.0.0.1:8080/perbank/我是cm?name=我是cm
將以上地址輸入到瀏覽器URL輸入框中,通過查看http 報文頭資訊我們可以看到瀏覽器是如何進行編碼的。下面是IE、Firefox、Chrome三個瀏覽器的編碼情況:
可以看到各大瀏覽器對“我是”的編碼情況如下:
|
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