遇到在URL中拼接中文的參數,后台拿到的數據為亂碼的問題,這里來說一下問題出現的原因與解決方法。
大家比較關心的應該是解決的方法,因此先說解決方法。
解決方法
解決的方法是在客戶端對這個中文參數進行編碼,然后服務端再進行響應的解碼就行了。
客戶端編碼(JavaScript)
var url = "contract!select.action?chineseParam=" + encodeURI(encodeURI("我是中文參數"));
注意:編碼的時候需要使用兩次encodeURI()方法,寫一個的后台輸出是???號,寫兩個的后台輸出則是%4d%5a這種,原因后面再說。
服務端解碼(Java)
String chineseParam = java.net.URLDecoder.decode(chineseParam, "UTF-8");
這樣就能實現中文參數的前后端傳遞了。
另外要注意的是,這種解決方法只有在拼接URL參數的場景下有效,如果是將參數傳遞放在請求體中,比如AJAX中的data,是不需要對中文參數進行手動編碼和解碼操作的,只需要保證前后端的編碼配置一致即可。
亂碼的產生原因與亂碼的簡單解析
聰明的你可能會發現,大多數的中文亂碼是通過GET方式產生的。這是因為GET方式有自動解碼的操作,相當於執行代碼:
URLDecoder.decode("%E6%B1%89%E5%AD%97", "編碼方式");
這是產生亂碼的根本原因。
Tomcat中亂碼產生的原因以及解決方法
在Tomcat的配置文件server.xml中,如果不配置解碼方式的話,就會按照默認的ISO-8859-1編碼進行解碼,具體可以參照Tomcat的文檔。
URIEncoding:This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
這也就意味着服務端會默認執行這樣的代碼:
URLDecoder.decode("%E6%B1%89%E5%AD%97", "ISO-8859-1");
因為瀏覽器通常是使用的UTF-8編碼,如果使用ISO解碼的話,就會出現亂碼。其中,【%E6%B1%89%E5%AD%97】是前端encodeURI("漢字") 的結果,此函數使用UTF-8編碼。
所以,Tomcat應該在配置文件中增加【URIEncoding="UTF-8"】的配置,避免亂碼的問題產生。
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8" />
亂碼時的問號?
前端處理漢字時用的encodeURI是按照UTF-8方式進行編碼,而此種編碼方式是一個漢字占3字節。而通過request取到的參數默認是通過ISO-8859-1的方式進行解碼的,在編碼表中找不到對應的字符就顯示【?】了,並且是3倍於漢字的個數,即3個【?】號代表一個漢字。
為什么通過兩次encodeURI可以解決亂碼問題
回到最上面的解決方法。
將【漢字】通過encodeURI或者encodeURIComponent編碼一次,會得到字符串【%E6%B1%89%E5%AD%97】(6個字節),此字符串使用UTF-8編碼的,如果此時用ISO-8859-1解碼,因為在ISO-8859-1的編碼表中找不到【E6】等字段對應的字符,所以是6個問號。
而第二次編碼則相當於對非中文字符【%E6%B1%89%E5%AD%97】進行編碼,得到的是字符串【%25E6%25B1%2589%25E5%25AD%2597】,此時服務端無論采用哪種解碼方式,都將得到【%E6%B1%89%E5%AD%97】字符,就可以被正確解析了。這樣,最后再通過UTF-8解碼該字符,就可以得到【漢字】了,相當於執行了以下代碼:
URLDecoder.decode("%E6%B1%89%E5%AD%97", "UTF-8");
所以通過兩次encodeURI就可以解決大部分的亂碼問題。
"人生最遺憾的,莫過於輕易地放棄了不該放棄的,卻固執地堅持了不該堅持的。"