起因
在編寫(xiě)servlet
代碼的時(shí)候,練習(xí)重定向和請(qǐng)求轉(zhuǎn)發(fā),發(fā)現(xiàn)重定向至html
頁(yè)面時(shí),頁(yè)面顯示中文正常,使用請(qǐng)求轉(zhuǎn)發(fā)至html
頁(yè)面時(shí),顯示中文亂碼,兩個(gè)servlet
都在doGet()
方法內(nèi)首先使用了resp.setCharacterEncoding("UTF-8");
設(shè)置編碼為UTF-8
。
核心代碼
各部分核心代碼如下:
重定向repRedirect
代碼:
@WebServlet("/repTest")
public class repRedirect extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String username=req.getParameter("username");
String password=req.getParameter("password");
if(username.equals("admin")&&password.equals("123456")){
resp.sendRedirect("/Unit7/welcome.html");
}else{
resp.sendRedirect("/Unit7/fail.html");
}
}
}
請(qǐng)求轉(zhuǎn)發(fā)repForward.java
代碼:
@WebServlet("/repForward")
public class repForward extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setContentType("text/html;charset=utf-8");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Content-Type","text/html;charset=UTF-8");
String username=req.getParameter("username");
String password=req.getParameter("password");
// if(username.isEmpty()&&password.isEmpty()){
// resp.sendRedirect("/Unit/welcome.html");
// }
if(username.equals("admin")&&password.equals("123456")){
req.getRequestDispatcher("/Unit7/welcome.html").forward(req,resp);
}else{
req.getRequestDispatcher("/Unit7/fail.html").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
跳轉(zhuǎn)的html頁(yè)面代碼
welcome.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>歡迎登陸</h3>
</body>
</html>
fail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>登陸失敗</h3>
</body>
</html>
訪(fǎng)問(wèn)效果
訪(fǎng)問(wèn)重定向頁(yè)面
http://127.0.0.1:8080/repTest?username=admin&password=123456
訪(fǎng)問(wèn)請(qǐng)求轉(zhuǎn)發(fā)頁(yè)面
http://127.0.0.1:8080/repForward?username=admin&password=123456
可以看出請(qǐng)求轉(zhuǎn)發(fā)出現(xiàn)了亂碼
我們?cè)俟室廨斿e(cuò)密碼,測(cè)試一下請(qǐng)求轉(zhuǎn)發(fā)失敗的頁(yè)面fail.html
http://127.0.0.1:8080/repForward?username=admin&password=1234
同樣亂碼
解決方法(1)
網(wǎng)上已經(jīng)有人給出了解決方法,即將請(qǐng)求轉(zhuǎn)發(fā)跳轉(zhuǎn)的html
編碼設(shè)置為GBK
,如下
重新訪(fǎng)問(wèn)請(qǐng)求轉(zhuǎn)發(fā)頁(yè)面
http://127.0.0.1:8080/repForward?username=admin&password=123456
此時(shí)亂碼恢復(fù)正常
fail.html
頁(yè)面并沒(méi)有修改為GBK
,我們?cè)L問(wèn)它試試,進(jìn)行對(duì)照比較
http://127.0.0.1:8080/repForward?username=admin&password=1234
依舊亂碼,可以看出是設(shè)置GBK
生效
原因分析
雖然這樣設(shè)置能夠讓頁(yè)面的亂碼正常,但是我們實(shí)際上是在servlet
里面設(shè)置了UTF-8
編碼,為什么頁(yè)面上還需要改為GBK
,而為什么請(qǐng)求轉(zhuǎn)發(fā)和重定向都設(shè)置了UTF-8
但只有請(qǐng)求轉(zhuǎn)發(fā)的頁(yè)面顯示亂碼了
在這篇博文中提到
因此,我認(rèn)為一個(gè)HTML文件在集成開(kāi)發(fā)創(chuàng)建時(shí)是UTF-8的格式,這個(gè)格式在IDE上應(yīng)該可以設(shè)置,但在打開(kāi)時(shí)是更具默認(rèn)編碼格式打開(kāi)的(即ANSI),因此會(huì)產(chǎn)生亂碼
而在我們的討論+猜測(cè)下,請(qǐng)求轉(zhuǎn)發(fā)的過(guò)程類(lèi)似于
圖中的GBK
指的是servlet默認(rèn)讀取文件的編碼方式,不同地區(qū)的電腦可能默認(rèn)不一樣
在另一篇博文對(duì)請(qǐng)求轉(zhuǎn)發(fā)詳細(xì)流程的介紹中也提到:
forward() 方法的處理流程:
● 清空用于存放響應(yīng)正文(響應(yīng)體)數(shù)據(jù)的緩沖區(qū)。
● 如果目標(biāo)組件為Servlet 或JSP,就調(diào)用它們的service() 方法,把該方法產(chǎn)生的響應(yīng)結(jié)果發(fā)送到客戶(hù)端,如果目標(biāo)組件為文件系統(tǒng)中的靜態(tài) html 文檔,就讀去文檔中的數(shù)據(jù)并把它發(fā)送到客戶(hù)端。
也就是說(shuō),請(qǐng)求轉(zhuǎn)發(fā)是有一個(gè)讀文檔數(shù)據(jù)的過(guò)程,側(cè)面論證了圖示
讀文檔數(shù)據(jù)涉及到了文檔原來(lái)的編碼和讀取文件的編碼方式,分析可得,出現(xiàn)亂碼的原因是,文檔本身的編碼是UTF-8
,但servlet讀取時(shí)是使用默認(rèn)編碼方式GBK
讀取的。
而我們?cè)趕ervlet中設(shè)置編碼的過(guò)程,也就是圖中的第二部分,所以不管我們用什么樣的方式設(shè)置UTF-8
,都只會(huì)讓只能使用GBK
解析的頁(yè)面亂碼
當(dāng)然這一部分還是猜測(cè),所以我們用另外兩種方法來(lái)驗(yàn)證我們的觀點(diǎn)
論證1
不管編碼解碼的結(jié)果如何,我們想要傳遞的數(shù)據(jù)字節(jié)流是不變的,它不會(huì)因?yàn)閬y碼字符就從登陸成功變成登陸失敗,換一句話(huà)說(shuō),不管用中文還是英文表達(dá)同一件事情,其內(nèi)核都是表達(dá)的這件事,并不會(huì)因?yàn)槭褂玫恼Z(yǔ)言而改變
既然文檔本身的編碼是UTF-8
,servlet讀取方式為GBK
,我們把文檔本身編碼修改為GBK
,如果上面的推斷是正確的,這樣做應(yīng)該就能夠讓亂碼正常
流程為:
GBK -> GBK -> 字符流 -> UTF-8 ->UTF-8頁(yè)面顯示
打開(kāi)VScode
,方便轉(zhuǎn)換編碼
可以看到文件此時(shí)保存的編碼為UTF-8
我們修改其為GBK
,點(diǎn)擊通過(guò)編碼保存
設(shè)置成GBK
可以見(jiàn)到此時(shí)文件保存編碼修改為了 GBK
這個(gè)時(shí)候再啟動(dòng)servlet
請(qǐng)求轉(zhuǎn)發(fā)
訪(fǎng)問(wèn) http://127.0.0.1:8080/repForward?username=admin&password=123456
顯示正常
側(cè)面反映了我們思考的過(guò)程是正確的
論證2
配置XML
文件
<jsp-config>
<jsp-property-group>
<url-pattern>*.html</url-pattern>
<page-encoding>UTF-8</page-encoding>
</jsp-property-group>
</jsp-config>
意思是,任何以html
結(jié)尾的URL請(qǐng)求的資源,都以UTF-8
格式打開(kāi),這里也就是修改servlet
的讀取文件編碼方式,此時(shí)將welcome.html
文件的編碼修改回UTF-8
重啟服務(wù),訪(fǎng)問(wèn)請(qǐng)求轉(zhuǎn)發(fā)頁(yè)面
http://127.0.0.1:8080/repForward?username=admin&password=123456
此時(shí)訪(fǎng)問(wèn)的頁(yè)面編碼依舊沒(méi)有亂碼,說(shuō)明jsp-config
起了作用,同時(shí)確實(shí)是因?yàn)?code>servlrt默認(rèn)讀取html
文件的編碼方式為GBK
為了驗(yàn)證真的是jsp-config
的作用,將其刪除再試一次
刪除后訪(fǎng)問(wèn)亂碼
證明確實(shí)servlet
默認(rèn)讀取html
文件編碼方式為GBK
,這個(gè)默認(rèn)方式不同的地區(qū)應(yīng)該是不同的,例如美國(guó)應(yīng)該是ASCII
總結(jié)
兩個(gè)論證中的方法也都能解決請(qǐng)求轉(zhuǎn)發(fā)至html
頁(yè)面亂碼的問(wèn)題
亂碼出現(xiàn)的原因是文件本身編碼和servlet
默認(rèn)讀取文件編碼方式不一樣而導(dǎo)致的
至于為什么重定向的頁(yè)面顯示中文是正常的,我們認(rèn)為是請(qǐng)求轉(zhuǎn)發(fā)和重定向這兩個(gè)功能的實(shí)現(xiàn)在servlet
中不一樣,比如請(qǐng)求轉(zhuǎn)發(fā)是需要讀取轉(zhuǎn)發(fā)的html
文件,重定向是讓用戶(hù)自己重新去訪(fǎng)問(wèn)html
頁(yè)面,缺少了默認(rèn)讀取的部分,具體的實(shí)現(xiàn)區(qū)別需要看servlet
的源代碼才能知道
參考鏈接
- https://blog.csdn.net/qq_27368993/article/details/83616090
- https://blog.csdn.net/qq_26164609/article/details/102826376
END
建了一個(gè)微信的安全交流群,歡迎添加我微信備注進(jìn)群
,一起來(lái)聊天吹水哇,以及一個(gè)會(huì)發(fā)布安全相關(guān)內(nèi)容的公眾號(hào),歡迎關(guān)注 ??
本文摘自 :https://www.cnblogs.com/