Java程序員遇上字符亂碼15-j2ee中的亂碼

從前面的介紹Url編碼文章中,我們了解了瀏覽器發(fā)起請(qǐng)求的字符處理過程。現(xiàn)在開始介紹tomcat服務(wù)器接收處理請(qǐng)求的過程。

httpClient模擬瀏覽器

在說tomcat服務(wù)器如何接收處理請(qǐng)求之前,我想先嘗試用httpClient工具來模擬瀏覽器發(fā)起請(qǐng)求的處理過程。其實(shí)這樣可以更加深刻理解瀏覽器。

演示:

1) 如果沒有url編碼就發(fā)送出去,那么服務(wù)器無法識(shí)別,于是報(bào)錯(cuò)。

2)? 如果先將請(qǐng)求中的參數(shù)值(value)進(jìn)行Urlencode后,這其實(shí)在模擬瀏覽器Get請(qǐng)求,服務(wù)器就正常識(shí)別了:

POST請(qǐng)求類似,不多演示。

tomcat處理請(qǐng)求值過程

Get和Post過來的鍵值對(duì),在我們調(diào)用request.getParameter("key")時(shí),tomcat會(huì)對(duì)value進(jìn)行轉(zhuǎn)碼。

我們知道鍵值對(duì)是經(jīng)過Url編碼的,這是client和server之間的行業(yè)約定。因此tomcat的處理也是會(huì)遵循此約定來處理。然而,Get和Post過來的數(shù)據(jù),tomcat雖然在value值編碼過程一樣,可是編碼的根據(jù)卻是不同點(diǎn)的。

Get請(qǐng)求:主要看tomcat的server.xml文件中的兩個(gè)參數(shù)配置:

URIEncoding :意思是,傳遞過來的value是UTF-8格式化得到的,因此tomcat會(huì)將utf-8字節(jié)轉(zhuǎn)化成unicode,進(jìn)而轉(zhuǎn)化為Utf-16內(nèi)碼。

useBodyEncodingForURI: 在默認(rèn)情況下,該參數(shù)為false。單詞中文意思大概是“按照request body字符的格式對(duì)待uri”。我們知道,常規(guī)情況下,get請(qǐng)求是沒有request body的??墒俏覀儾慌懦齈ost請(qǐng)求中URI也是帶有鍵值對(duì)的,當(dāng)這種少數(shù)情況出現(xiàn),tomcat就會(huì)根據(jù)request body 中的格式對(duì)待Uri鍵值對(duì)了。其實(shí)它的意思應(yīng)該是按照request請(qǐng)求頭中contentType指定的格式來對(duì)待uri鍵值對(duì)。一般情況下Request中我們都不會(huì)特地設(shè)置contentType,因?yàn)槲覀儽緛硪呀?jīng)知道客戶端發(fā)送過來的內(nèi)容會(huì)是什么格式的(因?yàn)榍耙粋€(gè)Response頭中我們已經(jīng)指定了)。如果沒有contentType指定,則tomcat會(huì)默認(rèn) value是 ISO8859-1格式的。

不過,這里有個(gè)細(xì)節(jié):URIEncoding 其實(shí)是受 useBodyEncodingForURI 牽制的。當(dāng)useBodyEncodingForURI = true ,則URIEncoding無效、tomcat只會(huì)根據(jù)Request頭中的ContentType對(duì)待鍵值對(duì)。當(dāng)=false,tomcat則只能根據(jù)URIEncoding來對(duì)待鍵值對(duì)。

例如:如果URIEncoding="gbk" useBodyEncodingForURI="true"都設(shè)置了,那么URIEncoding="gbk"不起作用。

如果我們沒有顯式指定URIEncoding 和 useBodyEncodingForURI,那么tomcat默認(rèn)鍵值對(duì)中的value是ISO8859-1格式的。

POST請(qǐng)求:tomcat只會(huì)根據(jù)request.setCharacterEncoding("CharsetName") 對(duì)待請(qǐng)求體(注意這里僅僅是針對(duì)請(qǐng)求體,而不是針對(duì)Post請(qǐng)求中的URI鍵值對(duì))。即,tomcat會(huì)認(rèn)為請(qǐng)求體字節(jié)是客戶端根據(jù)“CharsetName”格式化得來的,那么tomcat就會(huì)這樣轉(zhuǎn)碼:CharsetName --> Unicode -->Utf-16內(nèi)碼。

tomcat并不會(huì)根據(jù)request.setCharacterEncoding("CharsetName")處理Get請(qǐng)求,也不會(huì)根據(jù)URIEncode和useBodyEncodingForURI處理Post請(qǐng)求中的請(qǐng)求體。

tips1:上面說的結(jié)論,大家可以自己逐一測試,在此就不演示了。

綜上,tomcat對(duì)待Get、Post請(qǐng)求的處理是很分明的。既然那么分明,那么我們也得分明地設(shè)置好。一般地,我們會(huì)在Filter中request.setCharacterEncoding("CharsetName") 來獨(dú)立對(duì)待Post請(qǐng)求。

tips2:

其實(shí)上述的結(jié)論,我們可以直接從tomcat源碼中看出:org.apache.catalina.connector.Request#parseParameters()

注意:轉(zhuǎn)碼失敗容易丟失字節(jié)

假設(shè)客戶端通過sender=URLEncoder.encode("中文","GBK")對(duì)參數(shù)進(jìn)行編碼 再GET提交過來。如果server.xml中設(shè)置URIEncoding="utf-8",?沒有設(shè)置useBodyEncodingForURI="true"?,?那么,會(huì)使用utf-8對(duì)16進(jìn)制的參數(shù)進(jìn)行解碼,這時(shí)候?request.getParameter()獲得亂碼的中文參數(shù)值,并且通過new String(sender.getBytes("??utf-8??"), "gbk")也會(huì)亂碼,可能丟失字節(jié);原因我猜測如下:

按照GBK字符編碼,"中文" 是 四個(gè)字節(jié)的:"D6D0CEC4"。當(dāng)tomcat接收到數(shù)據(jù)后,以 utf-8來接收并解碼,會(huì)出現(xiàn)無法解析,

由上,我們?cè)谝?guī)劃轉(zhuǎn)碼配置時(shí)要謹(jǐn)慎。

響應(yīng)指定編碼

響應(yīng)體中指定編碼其實(shí)非常好理解。JVM中字符內(nèi)碼是UTF-16,當(dāng)需要輸出給client時(shí),我們會(huì)提前設(shè)置:response.setCharacterEncoding("CharsetName"),tomcat輸出前會(huì)將字符這樣轉(zhuǎn)碼:Utf-16內(nèi)碼--> Unicode -->"CharsetName",并且tomcat底層會(huì)幫我們?cè)O(shè)置響應(yīng)頭ContentType指定編碼為CharsetName。如此client就知道如何解析這字節(jié)流了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 編碼問題一直困擾著開發(fā)人員,尤其在 Java 中更加明顯,因?yàn)?Java 是跨平臺(tái)語言,不同平臺(tái)之間編碼之間的切換...
    x360閱讀 2,579評(píng)論 1 20
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,545評(píng)論 19 139
  • 文章大綱:1.為什么要編碼?2.各種編碼集介紹3.UTF-8編碼規(guī)則介紹4.編碼所涉及場景5.相關(guān)筆試題答案和分析...
    檸檬烏冬面閱讀 1,138評(píng)論 0 1
  • 1. 概述 本文主要包括以下幾個(gè)方面:編碼基本知識(shí),java,系統(tǒng)軟件,url,工具軟件等。 在下面的描述中,將以...
    騷的掉渣閱讀 1,453評(píng)論 0 0
  • 在上篇博客中LZ介紹了前面兩種場景(IO、內(nèi)存)中的java編碼解碼操作,其實(shí)在這兩種場景中我們只需要在編碼解碼過...
    皓云觀閱讀 508評(píng)論 2 1

友情鏈接更多精彩內(nèi)容