前言
前面其實已經把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)轉發是在服務器內部控制權的轉移,客戶端瀏覽器的地址欄不會顯示出轉向后的地址。
喜歡就點個“推薦”哦!