基本概念
有信息交換就會產生編碼、傳輸、解碼三個過程。編碼是信息從一種形式轉變成另一種形式的過程,正如人類的語言通過聲帶編碼,轉換成聲波。解碼是編碼的逆函數,耳膜接收聲波,通過腦神經解碼成人類文化所能理解的信息。
字符集是一種文化上下文下的所有文字符號集合,它的作用是規定了某個文化下的所有字符,以及該字符在信息交換系統下的表示方式,在計算機信息系統下是字節或01序列。本文會在某些時刻將字符集和編碼方案互用,以方便理解。
對于javaweb應用,狹隘的編碼解碼的過程可以簡單的理解為:編碼的過程是文本字符串信息編碼成01序列,解碼是將01序列恢復為文本字符串信息,具體編碼成什么樣的01序列是由編碼采用的字符集來決定的,也就是編碼方案。
亂碼是對信息采用的編碼方案無法理解,使用了錯誤的編碼方案對信息進行解碼造成的。如果要理解一段信息的真實意圖,就得知道信息采用的編碼方案,這是信息交換的密鑰,這就是為什么戰爭年代破解對方電報加密方式,實際上就是在破譯對方的編碼方案。
http協議層的編碼解碼
http協議層的字符集關系到http發送者和接送者采用什么字符集方案解析對方發送的內容。
瀏覽器端的編碼
請求端常規請求方式主要為form、url、ajax、http組件如HttpClientAPI。
瀏覽器存在文檔編碼方案charset的概念,文檔的編碼方案等同于文檔解碼方案,它對文檔中發生的請求編碼會產生影響。
影響form提交數據的編碼的因素包括:form的accept-charset屬性、html文檔的編碼方案即document.charset。其中,form的accept-charset是否能夠有效,依賴具體瀏覽器的實現,有些瀏覽器并不支持,如IE。文檔編碼方案可以通過document.charset來修改。
文檔內的url編碼,如iframe的src指定的url,以文檔編碼方案為準,地址欄的url的編碼方案完全取決于具體的瀏覽器實現,通過HttpClient組件發送請求時,url是能任意指定編碼方案的。
ajax發送http請求的url編碼方式完全取決于瀏覽器實現,一般支持以文檔編碼方案來決定,但是數據體統一采用utf-8,另外,雖然ajax可以指定header在contenttype說明編碼方案,但這種做法不會對url、數據體的編碼方案產生任何影響,甚至在有些瀏覽器中,最終contenttype中的編碼描述都無法真正影響。
另外,header的編碼方案是iso-8859-1,這個是http規范。
服務端的解碼
服務端的httpserver需要解碼的對象包括:header、url、數據體。
header解碼方案是iso-8859-1。
url解碼方案通常稱為URIEncoding,一般HttpServer會提供相應設置,標準servlet并不提供該接口。jetty默認utf-8字符集來解碼,但其他httpserver如tomcat會默認iso-8859-1。
數據體解碼在servlet中可以通過request.setCharacterEncoding來設置。一般的,有些httpserver會以characterEncoding>request請求頭字符集>utf-8的優先順序來決定數據體的解碼方案。
服務端的編碼
服務端httpserver需要編碼的對象是:header、數據體。
header的編碼方案同樣是iso-8859-1。
通常情況下,服務端必須要指定返回數據體的編碼方案且要在header中標注編碼方案,否則httpserver一般默認iso-8859-1對輸出進行編碼,而瀏覽器也無法得知返回數據體的編碼方案,只能自行猜測,完全依賴瀏覽器自己的實現。
response.setCharacterEncoding的職能是告訴httpserver數據體的編碼方案,并不會也不應該影響到header中的編碼方案的標注。response.setContentType會影響到header的編碼方案的標注,瀏覽器根據該標識決定解碼方案。對于一個健全的httpserver來說,在同時通過兩個方法指定了數據體編碼方案和header編碼方案標注的情況下,數據體編碼方案應該由后者決定,這樣使瀏覽器端得到的編碼信息和服務端真正編碼信息一致。另外,一定要注意的是這兩個指定編碼方案的方法必須在response創建輸出流之前調用,輸出流一旦創建,編碼方案無法后期指定。
瀏覽器端的解碼
瀏覽器端對返回進行解碼的對象包括:header、數據體。
header的解碼方案是iso-8859-1。
瀏覽器的數據體解碼方案依賴返回信息,瀏覽器首先從返回頭header中查找編碼方案標注,如果沒有標注,在得知返回內容為html內容的話,將從head的meta標簽中讀取,如果還沒找到,瀏覽器就不知道如何解碼,會消極的選擇一種解碼方案。
在理論上,推薦html文檔在meta中聲明編碼,且編碼的聲明一定要在文件開始的1024字節內完成,所以最好在head標簽開始時立即聲明。
文檔中通常都會有一些通過url下載的資源文件,如css和js文件,如果資源文件輸出時沒有在返回頭中指定明確的編碼方案,瀏覽器無法得知編碼方案,只能以上面介紹到的文檔編碼方案來進行解碼,這也是瀏覽器容錯的最佳策略。