出現亂碼的根本緣由是客戶端、服務端兩端編碼格式不一致致使的。程序員
客戶端:多數狀況下,客戶端的編碼格式是 UTF-8。
服務端:服務端會根據不一樣的請求方法使用不一樣的編碼格式。如:請求方法爲 POST 時,編碼格式爲 UTF-8;請求方法爲 GET 時,編碼格式爲 ISO8859-1。bash
當請求方法爲 POST 時,客戶端和服務端兩邊的編碼格式一致,因此不存在亂碼問題。所以此處着重看下如何解決當請求方法爲 GET 時的亂碼問題。服務器
解決方法倒也簡單,只不過須要客戶端和服務端配合:網絡
在向 URL 添加參數以前,先對目標參數進行兩次 encode,如 UTF-8:框架
String username = "xxx";
username = URLEncoder.encode(username,"UTF-8");
username = URLEncoder.encode(username,"UTF-8");
String url = xxxxx.xxx + "?username=" + username + "&password=" + password;
複製代碼
服務器在收到數據以後,只需將數據進行一次跟客戶端編碼格式同樣的 decode,如 UTF-8:編碼
String username = URLDecoder.decode(username, "UTF-8")
複製代碼
這樣處理以後,兩邊就不會再出現亂碼了。url
經過上面的分析可知,亂碼產生的主要緣由是客戶端、服務器兩邊編碼不一致形成的,即發送 GET 請求時,客戶端使用的是 UTF-8 編碼格式對 URL 中的參數進行編碼,而服務器在接收數據的時候,使用的是 ISO8859-1(解析 POST 請求時,服務器使用的編碼格式是 UTF-8 編碼格式)編碼格式對 URL 中的參數進行解碼。spa
ISO8859-1 跟 ASCII 碼同樣,都是單字節編碼,ISO8859-1 是從 ASCII 擴展而來的。ISO8859-1 將 ASCII 一個字節中剩餘的最後一位用了起來,也就是說,它比 ASCII 多了 128 個字符。另外,由於 ISO8859-1 是從 ASCII 擴展而來的,因此,ISO8859-1 兼容 ASCII。code
原數據:string
極速
複製代碼
客戶端第一次編碼,URLDecoder.decode(username, "UTF-8") 編碼以後:
%E6%9E%81%E9%80%9F
複製代碼
客戶端第二次編碼,URLDecoder.decode(username, "UTF-8") 編碼以後:
%25E6%259E%2581%25E9%2580%259F
複製代碼
客戶端發出的 URL:
http://192.168.31.148:8080/OkHttpServer/login?username=%25E6%259E%2581%25E9%2580%259F&password=123456
複製代碼
服務器接收的 URL:
http://192.168.31.148:8080/OkHttpServer/login?username=%25E6%259E%2581%25E9%2580%259F&password=123456
複製代碼
服務器第一次解碼,服務器接收到 GET 請求以後,默認會用 ISO8859-1 編碼格式解碼,解碼以後獲得:
http://192.168.31.148:8080/OkHttpServer/login?username=%E6%9E%81%E9%80%9F&password=123456
複製代碼
須要注意的是,服務器用 ISO8859-1 編碼格式解碼 URL 中的參數是自動完成的。
由於客戶端第一次用 URLDecoder.decode(username, "UTF-8") 編碼 URL 中參數以後,獲得的是 ASCII 碼,且 UTF-8 和 ISO8859-1 對 ASCII 的編碼結果是一致的,因此,客戶端第二次用 URLDecoder.decode(username, "UTF-8") 以後的結果能夠直接用 ISO8859-1 編碼格式解碼。
因爲服務器解碼以後的 URL 中的參數是用 UTF-8 編碼格式編碼的,因此,此時須要服務器再用 UTF-8 編碼格式解碼一次。
服務器第二次解碼,服務器用 UTF-8 編碼格式解碼以後獲得:
http://192.168.31.148:8080/OkHttpServer/login?username=極速&password=123456
複製代碼
若是客戶端程序員沒有顯式用 UTF-8 編碼格式編碼 URL 中的參數,服務端要如何處理才能獲取到原數據?
首先,分析下若是客戶端沒有用 UTF-8 編碼格式編碼 URL 中的參數,程序是如何執行的:
網絡請求框架會對 URL 中的參數進行一次 UTF-8 編碼:
URLDecoder.encode(username, "UTF-8")
複製代碼
服務器會對 URL 中的參數進行一次 ISO8859-1 編碼:
URLDecoder.decode(username, "ISO8859-1")
複製代碼
明白了執行流程以後,如何解決天然也就顯而易見了:
先轉回 ISO8859-1 解碼(decode)以前的結果,再轉會 UTF-8 編碼(encode)以前的結果。
具體操做步驟:
//1. 先轉回 ISO8859-1 解碼(decode)以前的結果
String temp = URLDecoder.encode(username, "ISO8859-1");
//2. 再轉會 UTF-8 編碼(encode)以前的結果
temp = URLDecoder.decode(username, "UTF-8")
複製代碼
由於 URL 中的參數經 UTF-8 編碼格式編碼以後獲得的結果在 ISO8859-1 字符集可能同樣也可能根本表示不了,這也是爲何 ASCII 碼經 UTF-8 編碼格式編碼以後的結果能夠用 ISO8859-1 編碼格式解碼。如,在 Unicode 字符集中,第 20013 個字符是「中」,而在 ISO8859-1 字符集中,一共纔有 256 個字符。字符「中」經 UTF-8 編碼以後的結果再經 ISO8859-1 解碼,不管如何也得不到正確答案的。