OkHTTP、Retrofit 中文亂碼解決方法

1. 亂碼出現的緣由是什麼?

出現亂碼的根本緣由是客戶端、服務端兩端編碼格式不一致致使的。程序員

2. 兩端的編碼格式通常是什麼?

客戶端:多數狀況下,客戶端的編碼格式是 UTF-8。
服務端:服務端會根據不一樣的請求方法使用不一樣的編碼格式。如:請求方法爲 POST 時,編碼格式爲 UTF-8;請求方法爲 GET 時,編碼格式爲 ISO8859-1。bash

3. 如何解決亂碼問題?

當請求方法爲 POST 時,客戶端和服務端兩邊的編碼格式一致,因此不存在亂碼問題。所以此處着重看下如何解決當請求方法爲 GET 時的亂碼問題。服務器

解決方法倒也簡單,只不過須要客戶端和服務端配合:網絡

3.1 客戶端須要作什麼?

在向 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;
複製代碼

3.2 服務端須要作什麼?

服務器在收到數據以後,只需將數據進行一次跟客戶端編碼格式同樣的 decode,如 UTF-8:編碼

String username = URLDecoder.decode(username, "UTF-8")
複製代碼

這樣處理以後,兩邊就不會再出現亂碼了。url

4. 爲何對 URL 中的參數進行兩次 encode 以後,就能夠解決亂碼問題了?

4.1 原理解析

經過上面的分析可知,亂碼產生的主要緣由是客戶端、服務器兩邊編碼不一致形成的,即發送 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
複製代碼

4.2 實際應用

若是客戶端程序員沒有顯式用 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")
複製代碼

4.3 爲何 URL 中的參數經 UTF-8 編碼格式編碼以後不能經過 ISO8859-1 編碼格式直接解碼呢?

由於 URL 中的參數經 UTF-8 編碼格式編碼以後獲得的結果在 ISO8859-1 字符集可能同樣也可能根本表示不了,這也是爲何 ASCII 碼經 UTF-8 編碼格式編碼以後的結果能夠用 ISO8859-1 編碼格式解碼。如,在 Unicode 字符集中,第 20013 個字符是「中」,而在 ISO8859-1 字符集中,一共纔有 256 個字符。字符「中」經 UTF-8 編碼以後的結果再經 ISO8859-1 解碼,不管如何也得不到正確答案的。

相關文章
相關標籤/搜索