老生常談之HTTP亂碼問題瀏覽器
對於get請求 在Servlet中調用request.setCharacterEncoding()設置編碼是沒有意義的無論你使用任何編碼方式對於你的數據解碼沒有任何影響tomcat
問題的引出,在tomcat9以後,無論你在setCharacterEncoding()設置什麼編碼都不會亂碼,哪怕是最基礎的ACSII,那有人就說了,那不挺好的,不亂嗎就好了唄;服務器
沒辦法,我喜歡問本身爲何,憑什麼不亂嗎,那設置這個編碼有什麼意義?socket
網上找了一下,只有問爲何亂碼,沒人問爲啥不亂碼,總有人會說設置編碼要放在獲取參數前,諸如此類,說的一套一套,人云亦云!post
帶着這個問題我寫了一個簡單的socket程序 目的就是想服務器發送一個HTTP請求手動控制編碼環節,測試
第一次測試,直接把中文寫在url中,最後用utf-8來發送給服務器編碼
服務器返回400請求解析失敗url
Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
這啥玩意?,原來RFC 3986中規定URL中只容許包含24個英文字母以及基本的字符,中文編碼後其對應的二進制超過了規定返回致使報錯,這一點很重要3d
這致使了咱們在URL中不能傳遞中文參數,那不行,得解決這個問題,因而就出現了URLEncodingcode
咱們想要在url中包含中文,就必須先把中文轉換爲基本的字符,其原理是字符轉爲16進制的字符而後在每一個字節前加一個%,就像下面這樣
結果:%e4%bd%a0
把這個字符拿到網頁中URL解碼
沒問題
注意這裏數據依然是字符串格式的,要想經過socket發送則必須在進行編碼,例如咱們要請求的地址爲/untitled3/TestServlet?username=你
咱們先要使用URLEncoding將你
轉爲符合RFC 3986要求的字符串,替換到本來的位置去
處理過的地址爲/untitled3/TestServlet?username=%e4%bd%a0
而後在把URL放到咱們的HTTP請求報文中
服務器端doGet方法
解析成功
ok服務器已經成功解析了中文可是注意我在服務器端指定的編碼爲ASCII,這也是我要解決的主要問題,
客戶端發送請求時數據一共通過了兩次編碼,
那麼咱們服務器端執行的這個setCharacterEncoding是用在那一次呢?
沒錯就是第一次,這就是爲何咱們不管設置那種編碼都不會致使亂碼的緣由了,
決定是否亂碼的核心在第二次,在上面的例子中服務器先是用ASCII來解析http整個數據包,其中的的URL爲/untitled3/TestServlet?username=%e4%bd%a0
對於這個URL而言任何編碼表都能解析,不會亂碼,接下里就須要將裏面的參數部分(問號後面的)拿出來進行反向URLEncoding,此時的反向解析就是最重要的要從字符串中提取16進制數據,在經過編碼表進行解碼 ,
而tomcat9中默認的解碼方式爲UTF-8,到這裏你應該明白了,爲何不亂嗎?
簡單的說:對於get請求而言,調用setCharacterEncoding是沒有任何意義的,若是咱們要控制URLEncoding的解碼方式,必須經過server.xml來修改
在囉嗦一下,對於post請求,咱們的數據是包含在請求體中的因此,上面的配置對於post請求沒有效果,那若是請求體中包含了中文怎麼辦,很簡單隻要與客戶端保持相同的解碼方式便可,使用request.setCharacterEncoding方法來設置,
還有問題,爲何post就能夠呢? 由於RFC 3986只是說URL中的字符字節必須在某個範圍內,沒有限制請求體中的數據範圍,因此對於請求體,你愛放中就放中文,
你也能夠這麼理解request.setCharacterEncoding只是用來設置請求體的解碼方式,對於url中的參數解碼方式就必須同 server.xml來配置
另外對於post服務器其實能夠不設置編碼只要客戶端post請求頭的ContentType中聲明瞭編碼便可,這也側面印證了post纔是用來給服務器傳遞數據的更優方法,
那爲何你們喜歡用get呢?不知道,或許是由於簡單?,累了就到這裏吧
補充下1樓的解決亂碼方案也是能夠的 其原理是將使用錯誤碼錶解碼的結果還原爲二進制,再用正確碼錶從新解碼 固然你無論用哪一種方式都必須與對方採用相同的編碼方式 只要理解了其中原理,那亂碼問題也就迎刃而解了