這次使用者可以在首頁選擇自己喜歡的顏色,進入對應的頁面。
選擇綠色,會進入綠色介面:
選擇紅色,會進入紅色介面:
好的,這裡我們會看到四個頁面:
index.jsp中選擇顏色,點擊按鈕後提交到test.jsp。
test.jsp取得使用者選擇的顏色,根據顏色值顯示對應的頁面。
如果選擇了紅色,就顯示red.jsp。
如果選擇了綠色,就顯示green.jsp。
在這裡例子裡,index.jsp,red.jsp,green.jsp中的內容都是一樣的,所有的玄機都在test.jsp中。
現在面臨的問題是如何在test.jsp決定實現red.jsp或者green.jsp,我們可以在forward和redirect中任選其一。
3.2. 如果用forward
test.jsp中需要這樣寫:
<%@ page contentType="text/html; charset=gb2312"%><% String color = request.getParameter("color"); if ("red".equals(color)) { request.getRequestDispatcher("red.jsp").forward(request, response); } else if ("green".equals(color)) { request.getRequestDispatcher("green.jsp").forward(request, response); }%>
略過取得參數與比較參數值不提,只關注forward的部分:
request.getRequestDispatcher("red.jsp").forward(request, response);
首先調用request的getRequestDispatcher()方法,獲得對應red.jsp的轉寄站,然後調用forward()方法執行請求轉寄。結果使用者看到的就是red.jsp中的結果了,一個紅色的頁面。
這裡請大家注意一下瀏覽器的url地址:
選擇紅色頁面時:
選擇綠色頁面時:
於是,無論轉寄至red.jsp還是green.jsp,地址欄上顯示的都是test.jsp。
這是為什麼呢?通過下面的流程圖會讓我們容易理解:
瀏覽器向test.jsp發送請求。
test.jsp計算客戶選擇的顏色,將請求轉寄至red.jsp。
red.jsp返迴響應給瀏覽器。
這下知道為什麼瀏覽器的地址沒有變化了吧?因為瀏覽器只是執行了對test.jsp的請求,test.jsp到red.jsp的部分是在伺服器內執行的,瀏覽器並不知道伺服器裡到底發生了什麼,它只知道自己獲得的響應是test.jsp發回來的,甚至不知道伺服器還有個red.jsp。
這就是請求轉寄forward了。例子見lingo-sample/03-01/。
3.3. 如果用redirect
test.jsp中需要這樣寫:
<%@ page contentType="text/html; charset=gb2312"%><% String color = request.getParameter("color"); if ("red".equals(color)) { response.sendRedirect("red.jsp"); } else if ("green".equals(color)) { response.sendRedirect("green.jsp"); }%>
略過取得參數與比較參數值不提,只關注redirect的部分:
response.sendRedirect("red.jsp");
response翻譯過來就是響應,代表著http響應。調用response的sendRedirect("red.jsp")方法,將頁面重新導向到red.jsp。
再請大家注意一下瀏覽器的url地址:
選擇紅色頁面時:
選擇綠色頁面時:
與forward不同,url地址一直在變化,紅色的時候顯示red.jsp,綠色的時候顯示green.jsp。
再看一下流程圖吧:
瀏覽器向test.jsp發送請求。
test.jsp計算客戶選擇的顏色,向瀏覽器發送一個頁面重新導向(redirect)的響應,響應中包含red.jsp的url地址。
瀏覽器根據頁面重新導向(redirect)響應中的red.jsp地址,再次向伺服器發送請求,這次請求的就是red.jsp了。
red.jsp執行,返迴響應。
redirect會觸發另一個請求響應流程,第二次請求的時候是由瀏覽器發起對red.jsp的請求,所以url地址改變了。
這就是頁面重新導向redirect了。例子見lingo-sample/03-02/。
3.4. forward和redirect的問題3.4.1. 絕對路徑與相對路徑
如果咱們使用的URL網址是以“/”開頭的,那麼這個網址就叫做絕對路徑。
如果咱們使用的URL網址不是“/”開頭的,那麼這個網址就叫做相對路徑。
3.4.1.1. 相對路徑
在相對路徑上,兩者的表現是相同的。
看看lingo-sample/03-03/這個例子,如果我們去請求relative/forward.jsp或redirect.jsp,然後從這裡再跳轉向它下面的result/result.jsp會怎樣呢?
forward的例子:
<%request.getRequestDispatcher("result/result.jsp").forward(request, response);%> 這裡的相對路徑就是result/result.jsp。
因為剛剛請求的test.jsp是在/03-03/relative/下,所以我們的當前路徑就是/03-03/relative/,執行forward的時候會尋找當前路徑下的result/result.jsp,找到之後便轉寄請求。
redirect的例子:
<%response.sendRedirect("result/result.jsp");%> 這裡的相對路徑也是result/result.jsp。
因為剛剛請求的test.jsp是在/03-03/relative/下,所以我們的當前路徑就是/03-03/relative/,執行redirect的時候會把當前路徑加上result/result.jsp,把結果作為重新導向的地址發送給瀏覽器,瀏覽器再去請求/03-03/relative/result/result.jsp,從而得到響應。
3.4.1.2. 絕對路徑
問題出現了,絕對路徑在forward和redirect中出現了差別,還是剛才的情況,但使用絕對路徑的時候寫法便不同了。
forward的例子:
<%request.getRequestDispatcher("/relative/result/result.jsp").forward(request, response);%> 這裡的絕對路徑就是/relative/result/result.jsp。
在本地測試時,forward把http://localhost:8080/03-03/當作根路徑,在它的基礎上計算絕對路徑。
這是由jsp的部署方式決定的,webapp裡可以放好多項目,為了讓這些項目可以互不影響、獨立運行,不能讓請求從一個項目直接在伺服器內部轉移到另一個項目。為了防止出現這種情況,在執行forward的時候乾脆把項目的路徑當作根目錄,開發人員看不到其他項目,也就不會出現問題了。
redirect的例子:
<%response.sendRedirect("/03-03/absolute/result/result.jsp");%> 這裡的絕對路徑卻是/03-03/absolute/result/result.jsp。
在本地測試時,redirect把http://localhost:8080/當作根路徑,在它的基礎上計算絕對路徑。
因為rct會edire讓瀏覽器重新發起一個新請求,所以不會攪亂伺服器裡多重專案之間的關係,也就不需要對它做限制,如果需要在多重專案之間進行跳轉,就只能使用redirect。不過因為重新發起了新的請求,上次請求的那些資料都會丟失,如果有什麼重要的資料,記得要重新設定。
3.4.2. forward導致找不到圖片
找不到圖片,找不到js指令碼,找不到css樣式表,都屬於這個問題。
要示範這個問題,是非常容易的,只需要滿足兩個條件:
forward前後的jsp頁面不在一個目錄下。
forward後的jsp頁面裡使用相對路徑引用一些資源,圖片,js指令碼,css樣式表什麼的。
03-04裡就類比了這樣一個環境,你進入http://localhost:8080/03-04/,選擇“有問題的”:
開啟03-04可以看到如下的目錄結構:
|--+ 03-04 |--- index.jsp |--- test.jsp |--+ result |--- success.jsp |--- failure.jsp |--- lingo.png
剛才咱們看到的頁面是failure.jsp,它裡邊顯示圖片的部分是:
<img src="lingo.png" />
這時候就有疑問了,lingo.png和failure.jsp明明在同一個目錄下,為什麼無法顯示。
現在請在無法顯示的圖片上,點擊滑鼠右鍵,選擇屬性,讓我們看一片的請求地址:
圖片的位置本來在http://localhost:8080/03-04/result/lingo.png,但請求的地址卻是http://localhost:8080/03-04/lingo.png。問題就是丟掉了中間的/result。
再試一次index.jsp上的“沒問題的”:
這次我們看到的頁面是success.jsp,它裡邊顯示圖片的部分是:
<img src="result/lingo.png" />
結果手工加上result這段路徑後就可以顯示圖片了。
這個問題還要追溯到瀏覽器對html的處理方式,在html裡包含的圖片,css樣式表,js指令碼,視頻等等外部資源,都需要瀏覽器再次向伺服器發起請求。
如果這些外部資源使用了相對路徑,瀏覽器就會在當前請求路徑的基礎上,加上相對路徑拼接出完整的http請求,發送給伺服器。這個例子中,我們請求http://localhost:8080/03-04/test.jsp,瀏覽器得到的當前路徑就是http://localhost:8080/03-04/,failure.jsp中圖片的相對路徑是lingo.png,那麼拼接的結果是http://localhost:8080/03-04/lingo.png。
不要怪瀏覽器太傻,是因為使用forward的時候瀏覽器並不清楚這些改變。它一直認為,既然自己請求的是test.jsp,返回的自然就是test.jsp的內容,那麼再使用test.jsp當作當前路徑去計算相對路徑當然沒有問題。是我們欺騙了瀏覽器,在伺服器偷偷改變了請求流向,返回了其他頁面的內容。
清楚了以上的請求流程,就知道如何應對這種問題了。
第一種方法:不要在不同目錄之間使用forward做請求轉寄,保證當前路徑不發生變化。
第二種方法:像上例一樣修改圖片路徑,或全部改為絕對路徑。
請根據實際需要進行選擇。