標籤:java 亂碼 java web 解決方案
引言:
在進行Web開始時,亂碼是我們最經常遇到也是最基本的問題,有經驗的程式猿很容易能解決,初學者則容易被泥潭困住。而且很多時候,我們即使解決了亂碼問題也是不明就裡,往往雲裡霧裡。
其實亂碼問題很簡單,就是用戶端和伺服器使用了不一樣的字元集導致的。也就是我們傳送檔案是用的字元編碼和解析檔案的編碼不一致。所以只要搞清楚了我們的檔案是怎麼被編碼和解碼的解決亂碼就很簡單了。分析亂碼,我們從請求亂碼和響應亂碼來分析,請求亂碼又需要根據GET和POST來單獨分析。
請求亂碼——GET
請求的編碼是由瀏覽器發出的,使用GET方法請求伺服器資訊時,根據HTTP協議規定,Request包是沒有請求體的(也就是Request Body不存在)。所以我們只能把請求參數放在URL中。因此使用GET方式與伺服器通訊,編碼方面我們關心的重點是瀏覽器對URL的編碼方式,和伺服器對URL的解碼過程。
關於URL
URL是我們經常接觸並非常簡單的一種技術,URL技術簡單到它其實就是一個字串。實際上URL的結構是很複雜的,只不過通常上用法比較簡單而已。關於URL的詳細介紹可以參考下面的文章:
我是傳送門!!!
URL的規範並定義在RFC 1738文檔中。通過URL我們獲得通訊協定、主機網域名稱、處理連接埠、應用路徑、路徑參數、查詢參數、頁面片段等資訊。比如:
http://user:[email protected]/a/b;q=1/c?d=2;sessionid=qewfewrwer#2
根據上面的URL,我們可以得到如下資訊:
Part |
Data |
伺服器API |
Scheme |
http |
用req. getScheme |
user |
user |
囧,不知道 |
pass |
pass |
囧,不知道 |
host address |
example.com |
req.getServerName |
port |
80 |
req.getServerPort |
path |
/a/b;q=1/c |
req.getContextPath |
query parameters |
d=2;sessionid=qwefewrwer |
req.getQueryString |
fragement |
2 |
|
開發時,我們經常用到的就是path和query parameters,這兩個參數,剩下的參數使用的比較少,不過在RESTful編碼中,像路徑參數可能會用到。
瀏覽器對Path部分的編碼
path資訊被用來匹配處理路徑,一般設計上很少在path中包括中文參數。RFC文檔對於path的編碼也沒有明確規定。但是據其他文章的介紹,瀏覽器對Path的編碼一般都會採用UTF-8編碼,最新的URI標準已經定義了URI的編碼採用UTF-8編碼。
定義:簡單說path部分就是應用路徑部分,就是URL去掉協議、網域名稱、連接埠和查詢資訊剩下的部分。
伺服器對Path部分的解碼:(三種方案)
通常上,我們的請求都會首先發給Web容器(下面以Tomcat為例),URL也會被Web容器解碼,對於Tomcat容器來說,我們可以在conf/server.xml的connector標籤中增加URL解碼參數,預設容器對URL的使用ISO-8859-1解碼。
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
上面的是Tomcat的預設設定,可以給標籤添加URIEncoding屬性來指定URL的解碼方案。(PS:標籤寫法是URI不是URL)
如果不想使用這種硬解碼方案,還可以指定另一個屬性:useBodyEncodingForURI,這個屬性用來告訴Web容器,如果request指定瞭解碼方案,則使用request.setCharacterEncoding指定的編碼來解碼URL。
第二種方案沒有經過測試,如果有需要可以嘗試下。詳細資料可以參考下面的Tomcat官方文檔:
http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q2
此外,如果不想修改容器的全域配置,畢竟有時候容器裡可能不止我們一個應用,那麼我們還可以採用下面的做法來提取參數:
String path = req.getServerPath();//自己手動提取,不適合配合架構path = new String(path.getBytes(“ISO8859-1”,”UTF-8”));//重新拼裝
上面的做法,我們要確定Web容器對URL的解碼用的是ISO8859-1,因為不排除其他人修改了容器配置或容器配置本身比較奇葩的可能。
瀏覽器對QueryParameter的編碼
查詢參數和Path是不一樣的,缺少查詢參數,web容器是可以定位到我們的處理常式的,但是缺少path就不行。另外,path和查詢參數的保留字元是不一樣的。
定義:簡單來說查詢參數就是path後面緊跟的?後面的部分,用&來串連各個查詢參數。
由於Path和查詢參數的不同,有些瀏覽器對查詢參數的編碼和path部分的編碼是不一致的。具體使用怎麼編碼的比較混亂,可以參考下下面的文章:
又一個傳送門,啦啦啦!
根據上面的文章總結的規律:
(1)Path部分或者說除查詢參數外的URL部分,各瀏覽器用UTF-8編碼;
(2)查詢參數,各瀏覽器根據作業系統編碼決定;
上面的文章比較老了,規律可能不實用了,但是也能說明一定問題。對於某些文章說的,查詢參數會根據頁面編碼來決定,我沒有做實驗,但是這種結論肯定是片面的。原因如下:
頁面的meta參數是用來向瀏覽器說明頁面編碼的,其次,在使用POST Method發送資料的時候,瀏覽器會根據meta的編碼來編碼Request Body。而Get方式,我們在沒有頁面的時候也可以發起,所以瀏覽器根本找不到Meta標籤,也就沒法參考頁面編碼。
瀏覽器對查詢參數到底使用哪種方式編碼的,我沒有找到專業、權威、可信的答案,但是我認為這個還是具體情況具體分析,做個小實驗就行了。畢竟時代在進步,廠商們統一使用UTF-8編碼的可能性比較大。而且後面有不依賴瀏覽器編碼的解決方案。
伺服器對QueryParameter的解碼
查詢參數也是URL的一部分,所以Web容器對查詢參數的解碼比較明智,解碼和path使用的是一樣的方案的編碼,所以解決方案也是一樣的。
出現亂碼:
在處理查詢參數時,我們常用req.getParameters();來擷取某個參數,這個方法背後很少有人關心它的工作原理,而且也沒必要。這一部分是最容易出現亂碼的,畢竟它裡面的參數可能是使用者輸入的,並不是我們設計的。在GET方式下,出現這種亂碼不要慌張,首先我們要分析出,瀏覽器對查詢參數到底採用了哪種編碼。方法簡單(也複雜),chrome下F12開啟開發人員工具
找到network標籤,可以看到Request URL中顯示的是k= %E4%B8%AD%E5%9B%BD,把%去掉,可以得到6個16進位數,百度下unicode碼錶,可以看到他們正好是“中”和“國”的unicode編碼。所以可以猜測瀏覽器使用的是UTF-8編碼。這種判斷方式需要對字元編碼比較熟悉。不過也不算很難,找點字元編碼的文章學學很容易就能看出規律來。
PS:不要通過瀏覽器的地址欄看URL編碼,很多瀏覽器的地址欄會對URL解碼顯示。
之後,伺服器端,首先確定下,你的Web容器對URL使用的解碼方案,然後相應的選擇String(param.getBytes(“ISO8895-1”,”UTF-8”))或者是useBodyEncodingForURI、URIEncoding方案就好了。
總結:
使用GET方式出現亂碼時,最主要的是找出瀏覽器對URL的編碼方式,如果使用JS編程時,在瀏覽器可以使用encodeURIComponent函數對中文參數進行編碼後再拼裝參數。Java端使用URLDecoder.decode方法解碼。JS端要進行兩次編碼,否則第一次的URL編碼會被Web容器解碼,擷取的參數仍有可能是亂碼。可以參考:
傳送!!!!!
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java Web亂碼分析及解決方案(一)——GET請求亂碼