當前位置:首頁 > IT技術 > Web編程 > 正文

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

起因

在編寫servlet代碼的時候,練習重定向和請求轉發(fā),發(fā)現(xiàn)重定向至html頁面時,頁面顯示中文正常,使用請求轉發(fā)至html頁面時,顯示中文亂碼,兩個servlet都在doGet()方法內首先使用了resp.setCharacterEncoding("UTF-8");設置編碼為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");
        }
    }
}

請求轉發(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);
    }
}

跳轉的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

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

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

img

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

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

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

img

同樣亂碼

解決方法(1)

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

img

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

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

img

此時亂碼恢復正常

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

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

img

依舊亂碼,可以看出是設置GBK生效

原因分析

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

在這篇博文中提到

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

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

img

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

在另一篇博文對請求轉發(fā)詳細流程的介紹中也提到:

forward() 方法的處理流程:
  ● 清空用于存放響應正文(響應體)數(shù)據(jù)的緩沖區(qū)。
  ● 如果目標組件為Servlet 或JSP,就調用它們的service() 方法,把該方法產生的響應結果發(fā)送到客戶端,如果目標組件為文件系統(tǒng)中的靜態(tài) html 文檔,就讀去文檔中的數(shù)據(jù)并把它發(fā)送到客戶端。

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

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

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

當然這一部分還是猜測,所以我們用另外兩種方法來驗證我們的觀點

論證1

不管編碼解碼的結果如何,我們想要傳遞的數(shù)據(jù)字節(jié)流是不變的,它不會因為亂碼字符就從登陸成功變成登陸失敗,換一句話說,不管用中文還是英文表達同一件事情,其內核都是表達的這件事,并不會因為使用的語言而改變

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

流程為:

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

打開VScode,方便轉換編碼

img

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

我們修改其為GBK,點擊通過編碼保存

img

設置成GBK

img

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

img

這個時候再啟動servlet請求轉發(fā)

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

顯示正常

img

側面反映了我們思考的過程是正確的

論證2

配置XML文件

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.html</url-pattern>
        <page-encoding>UTF-8</page-encoding>
    </jsp-property-group>
</jsp-config>

意思是,任何以html結尾的URL請求的資源,都以UTF-8格式打開,這里也就是修改servlet的讀取文件編碼方式,此時將welcome.html文件的編碼修改回UTF-8

img

img

重啟服務,訪問請求轉發(fā)頁面

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

此時訪問的頁面編碼依舊沒有亂碼,說明jsp-config起了作用,同時確實是因為servlrt默認讀取html文件的編碼方式為GBK

img

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

刪除后訪問亂碼

img

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

總結

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

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

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

參考鏈接

END

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

GIF GIF

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

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