tomcat中的字符集問題,測試以及總結(HTTP請求響應)
HTTP伺服器的通常作用可以理解成,接收來自瀏覽器的請求,讀取其中的資訊,並返回http格式的資料
其中,瀏覽器傳送的資料主要以三種形式傳遞,get方式提交的引數,post方式的引數,以及cookie中攜帶的引數三類
而伺服器端,生成http返回資料的形式主要有3種
其中,JSP在本質上與Servlet無異,一般可認為JSP在Servlet的基礎上,加上了Html的框架,不過如果把JSP程式碼中的HTML程式碼去掉,兩者基本就是一個東西了。
整個流程中,需要注意到字符集的動作,巨集觀而言,分為四步:
字符集問題出現在資訊從一級流通到另一級的過程中,也就是這4個動作當中。要規避字符集問題,需要了解四個動作中,分別的,其使用的字符集的確認方式。
主要包括,a中資料編碼採用何種字符集,b中如何確認解析時用的字符集,c中如何確定採用的字符集,d中如何確認解析時所用的字符集
以下就這四個問題展開討論
1、瀏覽器傳送資料時,採用哪種字符集?
瀏覽器發起一個請求,需要通過URL訪問
根據形式分為,直接在瀏覽器中輸入URL訪問,以及通過連結跳轉訪問,通過連結跳轉訪問則包括form表單以及超連結兩種形式
而無論哪種形式,最終瀏覽器都會將其包裝成HTTP格式進行訪問,也即如下格式:
POST /MyTestProject6/Test_Servlet_002 HTTP/1.1 Host: localhost Connection: keep-alive Content-Length: 39 Cache-Control: max-age=0 Origin: http://localhost Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Referer: http://localhost/MyTestProject6/Test_003.html Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: JSESSIONID=1420F43C4B8259D6FCCD46E87B095DB3; _ga=GA1.1.1822121407.1535547634
request資訊中,並不會指定編碼集。
那引數在傳遞過程中,對中文進行編碼的格式如何確定呢?
分兩種,直接在URL欄中輸入的中文,會以瀏覽器預設的編碼集進行編碼。
如此去訪問,火狐瀏覽器的結果是:
chrome瀏覽器的結果也是:
而其他的的訪問,也即是由頁面生成的URL,其內容的編碼格式取決於該頁面在編碼時使用的字符集
一定程度上,也就是頁面中的<meta charset="XXX">標籤,該標籤標註了該頁面的編碼格式
(不過會有特例,之後會進行說明)
當頁面中<meta charset="XXX">標籤預設時。瀏覽器會採用預設字符集對頁面進行編譯,這也是由這個頁面所產生的URL在生成request時所採用的字符集。
總結:瀏覽器傳送資料時,採用哪種字符集?
1、直接輸入URL的,依瀏覽器(chrome:utf-8,火狐:gbk)
2、頁面採用何種字符集編碼,則傳送資料時採用何種字符集
3、頁面未制定字符集時,chrome和火狐都預設使用GKB進行編碼
2、伺服器解析瀏覽器傳送的資料時,使用何種字符集?
上文提到,request中並不會攜帶關於request的內容由哪種字符集資訊。
因此,伺服器在解析資料時,隨緣。。。
比如某個使用者在URL中自己手動輸入中文引數,可能會影響。
但大多數URL都由伺服器自身的頁面發出,統一字符集的話,一般不會受到字符集問題的影響。
以下,以TOMCAT9為例,闡述如何應對GET請求,POST請求的處理方式。
總所周知,一條URL請求,會由瀏覽器包裝成HTTP格式,再發送到伺服器,因此伺服器接受到的資料是一個數據包,而非單單一個URL字串。
其中訪問路徑資訊path在第一行的中間,並且以get形式提交的資料顯示在了path當中。
tomcat7以上的版本,對於path的內容,會自動用utf-8去解碼。其也可以在server.xml中去配置。
而tomcat7以及以下的版本,預設的字符集依然是iso-8859-1。因此需要通過對應的格式去decode。
不過TOMCAT9自動預設UTF-8也有不好的地方,get請求的引數預設就直接UTF-8編碼了,也就是無論GBK還是UTF-8傳送過來都成了UTF-8。也就無法讀取GET裡面的內容了
而POST請求,TOMCAT並不會自動解碼,而預設的字符集是iso-8859-1,只需要設定其為對應的字符集即可。
總結:伺服器解析瀏覽器傳送的資料時,使用何種字符集?
可以設定,一般預設iso-8859-1,tomcat7以上get請求預設utf-8
3、伺服器返回資料時,使用何種字符集?
伺服器返回http資料時,一般有三種方式。
其中servlet中直接在response寫入的方式不難得出
如果未設定contentType,瀏覽器會使用預設的iso-8859-1進行解析
如果設定了contentType,比如:
response.setContentType("text/html;charset=utf-8"); response.getWriter().append("測試直接傳輸一箇中文");
則瀏覽器會使用對應的字符集進行解析。
BUT,需要注意的是,最終傳輸的資料的字符集需要與此時設定的字符集保持一致。
不一致的情況包括而不限於:
比如上述程式碼的.java檔案字符集是其他的,比如
而程式碼中設定的字符集為UTF-8,因此最終頁面會亂碼
這也是JSP跟HTTP頁面中需要注意的
在HTML頁面中,主要通過<meta charset="xxx"> 來控制頁面使用的字符集。
<!DOCTYPE html> <html> <head> <meta charset="gbk"> <title>Insert title here</title> </head> <body> 中文 </body> </html>
HTTP/1.1 200 Accept-Ranges: bytes ETag: W/"300-1536840847318" Last-Modified: Thu, 13 Sep 2018 12:14:07 GMT Content-Type: text/html Content-Length: 300 Date: Thu, 13 Sep 2018 12:14:10 GMT
不難看出,在訪問HTTP頁面時,其response的頭中,根本沒有標註其所使用的字符集
可以理解成,伺服器在響應返回html頁面時,並不會對其內容進行解析,而是直接傳送過去,因此伺服器也不知道該html的字符集
而瀏覽器在接收到response相應之後,也是在讀取response的content部分中的meta標籤才確定字符集。
HTML檔案同樣需要注意,最終傳輸的資料的字符集需要與此時設定的字符集保持一致。
預設時,chrome和火狐都預設使用gkb編碼(應對contentType為text/html)
在JSP檔案中,可以設定的字符集格式檔案包括pageEncoding,contentType,以及其html程式碼中的meta標籤。
不過contentType可以預設,contentType預設時伺服器會根據pageEncoding設定contentType中的charset。
比如:
<%@page pageEncoding="utf-8"%> 這是一箇中文
HTTP/1.1 200 Content-Type: text/html;charset=utf-8 Content-Length: 22 Date: Thu, 13 Sep 2018 12:22:49 GMT
而如果在已經有pageEncoding的情況下再去設定contentType的charset,後者會覆蓋前者,比如:
<%@page contentType="text/html;charset=gbk" pageEncoding="utf-8"%> 這是一箇中文
HTTP/1.1 200 Content-Type: text/html;charset=gbk Content-Length: 16 Date: Thu, 13 Sep 2018 12:24:04 GMT
當contentType設定了時,pageEncoding可以預設,此時會預設使用contentType中設定的字符集作為pageEncoding的字符集。
此時依然需要注意,最終傳輸的資料的字符集需要與此時設定的字符集保持一致。
但此時,如果字符集衝突,其衝突是在JSP的編譯階段,而不是在瀏覽器上的顯示階段。
比如,當我設定了pageEncoding,但是跟.jsp檔案採用的字符集不同時,其結果是在編譯而成的.java檔案中就出現了亂碼
比如
<%@page pageEncoding="utf-8"%> 這是個中文
則訪問亂碼
其.java檔案中就已經出現了亂碼
response.setContentType("text/html;charset=utf-8"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("\r\n"); out.write("���Ǹ�����\r\n");
當pageEncoding和contentType都預設時,JSP編譯器會很暈。。然後用iso-8859-1編碼
總之!!就是不用meta裡面設定的字符集。。。
<%@page%> <!DOCTYPE html> <html> <head> <meta charset="gbk" /> <title>Insert title here</title> </head> <body>我是個中文 </body> </html>
HTTP/1.1 200 Content-Type: text/html;charset=ISO-8859-1 Content-Length: 136 Date: Thu, 13 Sep 2018 12:34:10 GMT
總結:伺服器返回資料時,使用何種字符集?
1、servlet端需要設定contentType,注意與程式碼的編碼檔案格式保持一致,預設時瀏覽器使用iso-8859-1。
2、html檔案需要設定<meta charset>,注意與html檔案的編碼格式保持一致,預設時瀏覽器使用gbk。
3、jsp檔案需要設定contentType或pageEncoding,注意與jsp檔案的編碼格式保持一致,預設時瀏覽器使用gbk,但JSP編譯器會通過iso-8859-1去編譯。
4、瀏覽器解析資料時,採用何種字符集
依照上文
瀏覽器在解析時使用的字符集,依照response的head中的contentType中的charset引數。
當該引數預設時,會去尋找檔案中的<meta charset>標籤。
可以在servlet中測試如下:
ServerSocket ss = new ServerSocket(8081); while (true) { Socket s = ss.accept(); PrintWriter pw = new PrintWriter(s.getOutputStream()); pw.println("HTTP/1.1 200"); pw.println("Content-Type: text/html"); pw.println(); pw.println("<meta charset='utf-8'>"); pw.println("中文"); pw.flush(); s.close(); }
測試結果
也就是這樣子啦。。。