對於一個編解碼問題的思考
做過Java Web開發的人可能都遇到過這樣一個問題,當查詢條件中的引數為中文符號時,傳遞到後臺的引數不能正確解碼。我前一段時間遇到了這個問題,查詢到的解決方案是先在前臺使用JS對引數編碼兩次,然後再在後臺使用Java對其進行一次解碼。這個方案讓我感到很困惑,決定探個究竟。
先說一下開發環境,後臺使用的是JDK 7,應用伺服器使用的是Tomcat 7。
關於編解碼的小常識
在Unicode字元編碼中,每一個漢字字元都有一個唯一編碼,稱為碼點(Unicode code point)。表示一個Unicode值的十六進位制數通常在前面加上“U+”,例如“U+0041”代表字元“A”。
碼點僅僅是為字元賦予了一個唯一的ID值,是一種抽象,並沒有規定字元具體的編碼型別。在實際應用中,在使用字串的時候,需要指明其編碼型別,只談字元不談編碼型別就是耍流氓。(It does not make sense to have a string without knowing what encoding it uses. —— by Joel Spolsky)
最常見的字元編碼型別是UTF-8。網路傳輸中使用的是UTF-8編碼。
言歸正傳,在JS的encodeURIComponent()
方法中,使用一到四個轉義序列來表示字串中的每個字元的UTF-8編碼。另外, encodeURIComponent
還會轉義除了字母、數字、(、)、.、!、~、*、'、-和_之外的所有字元。
> encodeURIComponent("圖靈")
< "%E5%9B%BE%E7%81%B5" #UTF-8
當編碼後的字元傳送到後臺,後臺為什麼會出現亂碼呢? 後臺程式碼如下:
String name = request.getParameter("name");
name = URLDecoder.decode(name, "UTF-8");
原來,當使用request.getParameter()
獲得引數值的時候,此方法已經對傳遞過來的引數進行了一次解碼工作,而且很不幸的是,在Tomcat 7及以前的版本中,預設的編碼格式不是UTF-8,而是ISO-8859-1
,也就是Latin-1
。( 在Tomcat 8中,預設的編碼格式已經改為UTF-8了,參見這裡)。
注意:在使用Tomcat作為容器的時候,方法中出現的
HttpServletRequest
和HttpServletResponse
對應的實現類是由Tomcat提供的。
這樣就可以得到答案了。第一次使用encodeURIComponent
對字元進行編碼可以得到"%E5%9B%BE%E7%81%B5"
,第二次會將其中的%
進行轉義,轉義為%25
,其他字元則保持不變。當此引數值傳遞到後臺的時候,request.getParameter()
會首先對其進行一次解碼(使用ISO-8859-1
),將其還原為"%E5%9B%BE%E7%81%B5"
,然後就可以使用URLDecoder.decode(name, "UTF-8")
正確解碼了!
經過試驗發現,當將應用伺服器升級為Tomcat 8的時候,僅對其進行一次編碼操作就可以了。還有一個問題,Tomcat 8更改預設編碼格式會不會產生相容性問題?答案是不會,因為
UTF-8
是相容ISO-8859-1
的。
參考資料:
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q2
- http://stackoverflow.com/questions/469874/how-do-i-correctly-decode-unicode-parameters-passed-to-a-servlet/470320#470320
後記:
上述方法僅做為分析用途,不推薦在實際專案中使用。
實際上對於GET
請求,將server.xml中的URIEncoding
設定為UTF-8
或者將useBodyEncodingForURI
屬性設定為true即可。參見:https://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q2
相關文章
- 對於多個資料庫表對應一個Model問題的思考資料庫
- 思考一個問題
- 最近思考的一個問題
- (原)一個外行對ERP的問題的思考方式
- 一個關於Linq對引用型別元素集合去重問題的思考型別
- 一個外行對ERP的問題的思考方式=原創
- Android 7.0 startActivity()原始碼解析以及對幾個問題的思考:Android原始碼
- 一個GZIP編碼輸出問題
- 少編碼多思考:程式碼越多 問題越多
- [golang]一個複雜的中文編碼問題Golang
- 關於 http cache 的一個小問題以及引發的思考HTTP
- 一個關於ace-editor編輯器的問題
- [轉帖]一個NAT問題引起的思考
- 一個關於/root/.gvfs的問題解決?
- 關於JS的編碼轉換問題JS
- 關於Java編碼規範的問題Java
- 終於,解決了一個大問題
- 關於PWA落地問題的思考
- maven的編碼問題、解決和疑問Maven
- 二維碼問題上的一些思考
- 兩個關於許可權設定的問題思考
- 一個nvcc編譯的小問題編譯
- 對於人生的一些思考
- 請教一個關於hibernate對映oracle的問題Oracle
- 高手都進來歇歇~解決一個問題關於SE的問題
- 對IMP-00013問題的思考
- 關於教程的一個問題
- 程式導向,物件導向,函式式對同一個問題的思考方式物件函式
- Java編碼易疏忽的十個問題Java
- 關於JDON UTF版本中文編碼的問題
- 關於控制檯編碼的設定問題
- 一個延時任務問題引發的思考
- 關於php解構函式的一個有趣問題PHP函式
- 刨根問底:對於 self = [super init] 的思考
- 關於xml編碼問題在VB,PHP,JAVA下的解決方案XMLPHPJava
- 關於微博微信行銷的4個常見問題思考
- 提升解決問題能力的思考
- 關於Integer面試的一個問題面試