JavaWeb(一)Servlet中亂碼解決與轉發和重定向的區別


前言

  前面其實已經把Servlet中所有的內容都介紹完了,這篇講補充一點亂碼和重定向與轉發之間的區別!

一、request請求參數出現亂碼問題

1.1、get請求

  1)亂碼示例

  get請求的參數是在url后面提交過來的,也就是在請求行中。

  

  

  結果:

    

    Servlet_demo_0040是一個普通的Servlet,瀏覽器訪問它時,使用get請求方式提交了一個username=小明的參數值,在doGet中獲取該參數值,並且打印到控制台,發現出現亂碼

  2)出現亂碼原因

    前期知識:

      碼表:是一種規則,用來讓我們看得懂的語言轉換為電腦能夠認識的語言的一種規則,有很多中碼表,IS0-8859-1,GBK,UTF-8,UTF-16等一系列碼表,

         比如GBK,UTF-8,UTF-16都可以標識一個漢字,而如果要標識英文,就可以用IS0-8859-1等別的碼表。

      編碼:將我們看得懂的語言轉換為電腦能夠認識的語言。這個過程就是編碼的作用。

      解碼:將電腦認識的語言轉換為我們能看得懂得語言。這個過程就是解碼的作用

    這里只能夠代表經過一次編碼例子,有些程序中,會將一個漢字或者一個字母用不同的碼表連續編碼幾次,那么第一次編碼還是上面所說的作用,第二次編碼的話,

    就是將電腦能夠認識的語言轉換為電腦能夠認識的語言(轉換規則不同),那么該解碼過程,就必須要經過兩次解碼,也就是編碼的逆過程,下面這個例子就很好的說明了這個問題:

    瀏覽器使用的是UTF-8碼表,通過http協議傳輸,http協議只支持IS0-8859-1,到了服務器,默認也是使用的是IS0-8859-1的碼表,看圖:

    

    從上面的圖中可以看出,就是三個過程,經歷了兩次編碼,所以就需要進行兩次解碼:

      1)瀏覽器將"小明"使用UTF-8碼表進行編碼(因為小明這個是漢字,所以使用能標識中文的碼表,這也是我們可以在瀏覽器上可以手動設置的,如果使用了不能標識中文的碼表,那么就將會出現亂碼,

        因為碼表中找不到中文對應的計算機符號,就可能會用??等其他符號表示),編碼后得到的為 1234 ,將其通過http協議傳輸。

      2)在http協議傳輸,只能用ISO-8859-1碼表中所代表的符號,所以會將我們原先的1234再次進行一次編碼,這次使用的是ISO-8859-1,得到的為 ???? ,然后傳輸到服務器。

      3)服務器獲取到該數據是經過了兩次編碼后得到的數據,所以必須跟原先編碼的過程逆過來解碼,先是UTF-8編碼,然后在ISO-8859-1編碼,那么解碼的過程,就必須是先ISO-8859-1解碼,然后在用UTF-8解碼,

        這樣就能夠得到正確的數據。????.getBytes("ISO-8859-1");//第一次解碼,轉換為電腦能夠識別的語言, new String(1234,"UTF-8");//第二次解碼,轉換為我們認識的語言

  3)解決方法

    

    結果:

      

1.2、post請求

  post請求方式的參數是在請求體中,相對於get請求簡單很多,沒有經過http協議這一步的編碼過程,所以只需要在服務器端,設置服務器解碼的碼表跟瀏覽器編碼的碼表是一樣的就行了,

  在這里瀏覽器使用的是UTF-8碼表編碼,那么服務器端就設置解碼所用碼表也為UTF-8就OK了。

  設置服務器端使用UTF-8碼表解碼:

    request.setCharacterEncoding("UTF-8");  //命令Tomcat使用UTF-8碼表解碼,而不用默認的ISO-8859-1了。

  所以在很多時候,在doPost方法的第一句,就是這句代碼,防止獲取請求參數時亂碼

總結請求參數亂碼問題:

  get請求和post請求方式的中文亂碼問題處理方式不同

  get:請求參數在請求行中,涉及了http協議,手動解決亂碼問題,知道出現亂碼的根本原因,對症下葯,其原理就是進行兩次編碼,兩次解碼的過程

    new String(xxx.getBytes("ISO-8859-1"),"UTF-8");

  post:請求參數在請求體中,使用servlet API解決亂碼問題,其原理就是一次編碼一次解碼,命令tomcat使用特定的碼表解碼。

    request.setCharaterEncoding("UTF-8");

二、response響應回瀏覽器出現中文亂碼

  首先介紹一下,response對象是如何向瀏覽器發送數據的。兩種方法,一種getOutputStream,一種getWrite:  

    ServletOutputStream getOutputStream();  //獲取輸出字節流,提供write() 和 print() 兩個輸出方法。

    PrintWriter getWrite();  //獲取輸出字符流,提供write() 和 print()兩個輸出方法。

  print()方法底層都是使用write()方法的,相當於print()方法就是將write()方法進行了封裝,使開發者更方便快捷的使用,想輸出什么,就直接選擇合適的print()方法,而不用考慮如何轉換字節。  

2.1、ServeltOutputStream getOutputStream(); 

   

  結果:不能直接輸出中文,直接輸出中文會報異常    

    

  報異常的源碼分析:

    

  解決方法:

    resp.getoutputStream().write("哈哈哈,我要輸出到瀏覽器".getBytes("UTF-8"));

    將要輸出的漢字先用UTF-8進行編碼,而不用讓tomcat來進行編碼,這樣如果瀏覽器用的是UTF-8碼表進行解碼的話,那么就會正確輸出,如果瀏覽器用的不是UTF-8,那么還是會出現亂碼,

    所以說這個關鍵要看瀏覽器用的什么碼表,這個就不太好,這里還要注意一點,就是使用的是write(byte)方法,因為print()方法沒有輸出byte類型的方法。

    測試:因為我用的是谷歌瀏覽器它默認使用的是GB2312所以這里使用的是GB2312

      

      

2.2、PrintWriter getWrite();

  直接輸出中文,不會報異常,但是肯定會報異常,因為用ISO-8859-1的碼表不能標識中文,一開始就是錯的,怎么解碼編碼讀沒用了。

  有三種方法來讓其正確輸出中文:

  1)使用Servlet API  response.setCharacterEncoding()

    response.setCharacterEncoding("UTF-8");  //讓tomcat將我們要響應到瀏覽器的中文用UTF-8進行編碼,而不使用默認的ISO-8859-1了,這個還是要取決於瀏覽器是不是用的UTF-8的碼表,跟上面的一樣有缺陷。

    測試:因為我用的是谷歌瀏覽器它默認使用的是GB2312所以這里使用的是GB2312

     

  2)通知tomcat和瀏覽器都使用同一張碼表

    response.setHeader("content-type","text/html;charset=uft-8");  //手動設置響應內容,通知tomcat和瀏覽器使用utf-8來進行編碼和解碼。  

      charset=uft-8就相當於response.setCharacterEncoding("UTF-8");//通知tomcat使用utf-8進行編碼

      response.setHeader("content-type","text/html;charset=uft-8");//合起來,就是既通知tomcat用utf-8編碼,又通知瀏覽器用UTF-8進行解碼。

    response.setContentType("text/html;charset=uft-8");  //使用Servlet API 來通知tomcat和強制瀏覽器使用UTF-8來進行編碼解碼,這個的底層代碼就是上一行的代碼,進行了簡單的封裝而己

      

  3)通知tomcat,在使用html<meta>通知瀏覽器 (html源碼),注意:<meta>建議瀏覽器應該使用編碼,不能強制要求

    

    所以response在響應時,只要通知tomcat和瀏覽器使用同一張碼表,一般使用第二種方法,那么就可以解決響應的亂碼問題了

三、總結請求和響應的亂碼

3.1、請求亂碼 

  get請求:

    經過了兩次編碼,所以就要兩次解碼

      第一次解碼:xxx.getBytes("ISO-8859-1");得到yyy

      第二次解碼:new String(yyy,"utf-8");

    連續寫:new String(xxx.getBytes("ISO-8859-1"),"UTF-8");

  post請求:

    只經過一次編碼,所以也就只要一次解碼,使用Servlet API request.setCharacterEncoding();

    request.setCharacterEncoding("UTF-8");  //不一定解決,取決於瀏覽器是用什么碼表來編碼,瀏覽器用UTF-8,那么這里就寫UTF-8。

3.2、響應亂碼

  getOutputStream();:

    使用該字節輸出流,不能直接輸出中文,會出異常,要想輸出中文,解決方法如下

    解決:getOutputStream().write(xxx.getBytes("UTF-8"));  //手動將中文用UTF-8碼表編碼,變成字節傳輸,變成字節后,就不會報異常,並且tomcat也不會在編碼,因為已經編碼過了,所以到瀏覽器后,

                                如果瀏覽器使用的是UTF-8碼表解碼,那么就不會出現中文亂碼,反之則出現中文亂碼,所以這個方法,不能完全保證中文不亂碼

  getWrite();:

    使用字符輸出流,能直接輸出中文,不會出異常,但是會出現亂碼。能用三種方法解決,一直使用第二種方法

    解決:通知tomcat和瀏覽器使用同一張碼表。

    response.setContentType("text/html;charset=utf-8");  //通知瀏覽器使用UTF-8解碼 

    通知tomcat和瀏覽器使用UTF-8編碼和解碼。這個方法的底層原理是這句話:response.setHeader("contentType","text/html;charset=utf-8"); 

  注意:getOutputStream()和getWrite() 這兩個方法不能夠同時使用,一次只能使用一個,否則報異常     

四、Servlet中請求和轉發的區別

 4.1、使用方式 

  1)在servlet中調用轉發、重定向的語句如下:

request.getRequestDispatcher("new.jsp").forward(request, response); //轉發到new.jsp
response.sendRedirect("new.jsp"); //重定向到new.jsp

  2)在jsp頁面中你也會看到通過下面的方式實現轉發:

<jsp:forward page="apage.jsp" />

  當然也可以在jsp頁面中實現重定向:

<%response.sendRedirect("new.jsp"); %> //重定向到new.jsp

  示例:

//獲取username信息
String username=request.getParameter("username");
 //轉發與重定向
 if(username.equals("admin")){
  //提示用戶已存在,不能注冊
  request.setAttribute("message","該用戶已存在,不能注冊");//添加提示信息,需要在userCreate.jsp頁面中進行提示信息展示
  request.getRequestDispatcher("userCreate.jsp").forward(request, response);//轉法
 }else{
  //提示注冊成功
  request.setAttribute("message","注冊成功");
  response.sendRedirect("indext.jsp");//重定向
 }

4.2、區別:重定向和轉發的工作流程

  

  1)轉發的工作流程‘

    第一步:客戶瀏覽器發送http請求
    第二步:web服務器接受此請求    
    第三步:調用內部的一個方法在容器內部完成請求處理和轉發動作  
    第四步:將目標資源發送給客戶;在這里,轉發的路徑必須是同一個web容器下的url,其不能轉向到其他的web路徑上去,中間傳遞的是自己的容器內的request。在客戶瀏覽器路徑欄顯示的仍然是其第一次訪問的路徑,
        也就是說客戶是感覺不到服務器做了轉發的。轉發行為是瀏覽器只做了一次訪問請求。 

  2)重定向的工作流程

    

    第一步:客戶瀏覽器發送http請求
    第二步:web服務器接受后發送302狀態碼響應及對應新的location給客戶瀏覽器
    第三步:客戶瀏覽器發現是302響應,則自動再發送一個新的http請求,請求url是新的location地址
    第四步:服務器根據此請求尋找資源並發送給客戶。在這里location可以重定向到任意URL,既然是瀏覽器重新發出了請求,則就沒有什么request傳遞的概念了。
        在客戶瀏覽器路徑欄顯示的是其重定向的路徑,客戶可以觀察到地址的變化的。重定向行為是瀏覽器做了至少兩次的訪問請求的

  總結:一句話,轉發是服務器行為,重定向是客戶端行為

4.3、區別:請求次數

  1)重定向

    重定向,其實是兩次request

    第一次,客戶端request   A,服務器響應,並response回來,告訴瀏覽器,你應該去B。這個時候IE可以看到地址變了,而且歷史的回退按鈕也亮了。重定向可以訪問自己web應用以外的資源。在重定向的過程中,傳輸的信息會被丟失

    舉例:          

    response.sendRedirect("loginsuccess.jsp"); 

  2)轉發

    轉發是一次request  

    請求轉發是服務器內部把對一個request/response的處理權,移交給另外一個

    對於客戶端而言,它只知道自己最早請求的那個A,而不知道中間的B,甚至C、D。傳輸的信息不會丟失。  

     RequestDispatcher dis=request.getRequestDispatcher(“loginsuccess.jsp”);
       dis.forward(request,response);

 4.4、生活中的例子說明區別

  

  假設你去辦理某個執照
  重定向:你先去了A局,A局的人說:“這個事情不歸我們管,去B局”,然后,你就從A退了出來,自己乘車去了B局。
  轉發:你先去了A局,A局看了以后,知道這個事情其實應該B局來管,但是他沒有把你退回來,而是讓你坐一會兒,自己到后面辦公室聯系了B的人,讓他們辦好后,送了過來。

五、總結Servlet中請求和轉發的區別

  1)重定向的執行過程:Web服務器向瀏覽器發送一個http響應--》瀏覽器接受此響應后再發送一個新的http請求到服務器--》服務器根據此請求尋找資源並發送給瀏覽器。它可以重定向到任意URL,不能共享request范圍內的數據
  2)重定向是在客戶端發揮作用,通過新的地址實現頁面轉向
  3)重定向是通過瀏覽器重新請求地址,在地址欄中可以顯示轉向后的地址
  4)轉發過程:Web服務器調用內部方法在容器內部完成請求和轉發動作--》將目標資源發送給瀏覽器,它只能在同一個Web應用中使用,可以共享request范圍內的數據
  5)轉發是在服務器端發揮作用,通過forward()方法將提交信息在多個頁面間進行傳遞
  6)轉發是在服務器內部控制權的轉移,客戶端瀏覽器的地址欄不會顯示出轉向后的地址。

 

喜歡就點個“推薦”哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM