當(dāng)前位置:首頁 > IT技術(shù) > Web編程 > 正文

Servlet請求轉(zhuǎn)發(fā)至html頁面中文亂碼問題與分析
2021-09-23 06:48:51

起因

在編寫servlet代碼的時(shí)候,練習(xí)重定向和請求轉(zhuǎn)發(fā),發(fā)現(xiàn)重定向至html頁面時(shí),頁面顯示中文正常,使用請求轉(zhuǎn)發(fā)至html頁面時(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");
        }
    }
}

請求轉(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頁面代碼

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>

訪問效果

訪問重定向頁面

http://127.0.0.1:8080/repTest?username=admin&password=123456

img

訪問請求轉(zhuǎn)發(fā)頁面

http://127.0.0.1:8080/repForward?username=admin&password=123456

img

可以看出請求轉(zhuǎn)發(fā)出現(xiàn)了亂碼

我們再故意輸錯密碼,測試一下請求轉(zhuǎn)發(fā)失敗的頁面fail.html

http://127.0.0.1:8080/repForward?username=admin&password=1234

img

同樣亂碼

解決方法(1)

網(wǎng)上已經(jīng)有人給出了解決方法,即將請求轉(zhuǎn)發(fā)跳轉(zhuǎn)的html編碼設(shè)置為GBK,如下

img

重新訪問請求轉(zhuǎn)發(fā)頁面

http://127.0.0.1:8080/repForward?username=admin&password=123456

img

此時(shí)亂碼恢復(fù)正常

fail.html頁面并沒有修改為GBK,我們訪問它試試,進(jìn)行對照比較

http://127.0.0.1:8080/repForward?username=admin&password=1234

img

依舊亂碼,可以看出是設(shè)置GBK生效

原因分析

雖然這樣設(shè)置能夠讓頁面的亂碼正常,但是我們實(shí)際上是在servlet里面設(shè)置了UTF-8編碼,為什么頁面上還需要改為GBK,而為什么請求轉(zhuǎn)發(fā)和重定向都設(shè)置了UTF-8但只有請求轉(zhuǎn)發(fā)的頁面顯示亂碼了

在這篇博文中提到

因此,我認(rèn)為一個(gè)HTML文件在集成開發(fā)創(chuàng)建時(shí)是UTF-8的格式,這個(gè)格式在IDE上應(yīng)該可以設(shè)置,但在打開時(shí)是更具默認(rèn)編碼格式打開的(即ANSI),因此會產(chǎn)生亂碼

而在我們的討論+猜測下,請求轉(zhuǎn)發(fā)的過程類似于

img

圖中的GBK指的是servlet默認(rèn)讀取文件的編碼方式,不同地區(qū)的電腦可能默認(rèn)不一樣

在另一篇博文對請求轉(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ā)送到客戶端,如果目標(biāo)組件為文件系統(tǒng)中的靜態(tài) html 文檔,就讀去文檔中的數(shù)據(jù)并把它發(fā)送到客戶端。

也就是說,請求轉(zhuǎn)發(fā)是有一個(gè)讀文檔數(shù)據(jù)的過程,側(cè)面論證了圖示

讀文檔數(shù)據(jù)涉及到了文檔原來的編碼和讀取文件的編碼方式,分析可得,出現(xiàn)亂碼的原因是,文檔本身的編碼是UTF-8,但servlet讀取時(shí)是使用默認(rèn)編碼方式GBK讀取的。

而我們在servlet中設(shè)置編碼的過程,也就是圖中的第二部分,所以不管我們用什么樣的方式設(shè)置UTF-8,都只會讓只能使用GBK解析的頁面亂碼

當(dāng)然這一部分還是猜測,所以我們用另外兩種方法來驗(yàn)證我們的觀點(diǎn)

論證1

不管編碼解碼的結(jié)果如何,我們想要傳遞的數(shù)據(jù)字節(jié)流是不變的,它不會因?yàn)閬y碼字符就從登陸成功變成登陸失敗,換一句話說,不管用中文還是英文表達(dá)同一件事情,其內(nèi)核都是表達(dá)的這件事,并不會因?yàn)槭褂玫恼Z言而改變

既然文檔本身的編碼是UTF-8,servlet讀取方式為GBK,我們把文檔本身編碼修改為GBK,如果上面的推斷是正確的,這樣做應(yīng)該就能夠讓亂碼正常

流程為:

GBK -> GBK -> 字符流 -> UTF-8 ->UTF-8頁面顯示

打開VScode,方便轉(zhuǎn)換編碼

img

可以看到文件此時(shí)保存的編碼為UTF-8

我們修改其為GBK,點(diǎn)擊通過編碼保存

img

設(shè)置成GBK

img

可以見到此時(shí)文件保存編碼修改為了 GBK

img

這個(gè)時(shí)候再啟動servlet請求轉(zhuǎn)發(fā)

訪問 http://127.0.0.1:8080/repForward?username=admin&password=123456

顯示正常

img

側(cè)面反映了我們思考的過程是正確的

論證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請求的資源,都以UTF-8格式打開,這里也就是修改servlet的讀取文件編碼方式,此時(shí)將welcome.html文件的編碼修改回UTF-8

img

img

重啟服務(wù),訪問請求轉(zhuǎn)發(fā)頁面

http://127.0.0.1:8080/repForward?username=admin&password=123456

此時(shí)訪問的頁面編碼依舊沒有亂碼,說明jsp-config起了作用,同時(shí)確實(shí)是因?yàn)?code>servlrt默認(rèn)讀取html文件的編碼方式為GBK

img

為了驗(yàn)證真的是jsp-config的作用,將其刪除再試一次

刪除后訪問亂碼

img

證明確實(shí)servlet默認(rèn)讀取html文件編碼方式為GBK,這個(gè)默認(rèn)方式不同的地區(qū)應(yīng)該是不同的,例如美國應(yīng)該是ASCII

總結(jié)

兩個(gè)論證中的方法也都能解決請求轉(zhuǎn)發(fā)至html頁面亂碼的問題

亂碼出現(xiàn)的原因是文件本身編碼和servlet默認(rèn)讀取文件編碼方式不一樣而導(dǎo)致的

至于為什么重定向的頁面顯示中文是正常的,我們認(rèn)為是請求轉(zhuǎn)發(fā)和重定向這兩個(gè)功能的實(shí)現(xiàn)在servlet中不一樣,比如請求轉(zhuǎn)發(fā)是需要讀取轉(zhuǎn)發(fā)的html文件,重定向是讓用戶自己重新去訪問html頁面,缺少了默認(rèn)讀取的部分,具體的實(shí)現(xiàn)區(qū)別需要看servlet的源代碼才能知道

參考鏈接

END

建了一個(gè)微信的安全交流群,歡迎添加我微信備注進(jìn)群,一起來聊天吹水哇,以及一個(gè)會發(fā)布安全相關(guān)內(nèi)容的公眾號,歡迎關(guān)注 ??

GIF GIF

本文摘自 :https://www.cnblogs.com/

開通會員,享受整站包年服務(wù)立即開通 >