重新導向(Rediret) 到另一個視圖
在前面我們提到過,一個控制器通常會返回視圖名,然後由視圖解析器解析到某種視圖實現。對於像JSP這樣實際上由Servlet/JSP引擎處理的視圖,我們通常使用InternalResourceViewResolver和InternalResourceView。這種視圖實現最終會調用Servlet API的RequestDispatcher.forward(..)方法或RequestDispatcher.include()方法將使用者指向最終頁面。對於別的視圖技術而言(比如Velocity、XSLT等等), 視圖本身就會產生返回給使用者的內容。
有些時候,在視圖顯示以前,我們可能需要給使用者發一個HTTP redirect重新導向指令。比如,一個控制器成功的處理了一個表單提交(資料以HTTP POST的方式發送),它最終可能委託給另一個控制器來完成剩下的工作。在這種情況下,如果我們使用內部forward,接手工作的那個控制器將會得到所有以POST方式提交的表單資料,這可能會引起潛在的混淆,幹擾那個控制器的正常工作。 另一個在顯示視圖之前返回HTTP redirect的原因是這可以防止使用者重複提交同一表單。具體一點講,瀏覽器先用POST的方式提交表單,然後它接收到重新導向的指令,它繼續用GET的方式去下載新的頁面。從瀏覽器的角度看,這個新的頁面不是POST的返回結果,而是GET的。這樣,使用者不可能在點擊重新整理的時候不小心再次提交表單,因為重新整理的結果是再次用GET 去下載表單提交後的結果頁面,而不是重新提交表單。
RedirectView
在控制器中強制重新導向的方法之一是讓控制器產生並返回一個RedirectView的執行個體。在這種情況下,DispatcherServlet不會使用通常的視圖解析機制,既然它已經拿到了一個(重新導向)視圖,它就讓這個視圖去做剩下的工作。
RedirectView會調用HttpServletResponse.sendRedirect()方法,其結果是給使用者的瀏覽器發回一個HTTP redirect。所有的模型屬性都被轉換成以HTTP請求的訪問參數。這意味著這個模型只能包含可以被簡便的轉換成string形式的HTTP請求訪問參數的對象,比如String或者可以被轉換成String的類型。
如果你使用RedirectView視圖,並且它是由控制器產生的,重新導向的URL最好是用Spring所提供的IoC功能注射到控制器裡。這樣這個URL就可以和視圖名一起在上下文中被聲明,而不是固化在控制器內。
redirect:首碼
儘管RedirectView幫我們達到了目的,但是如果控制器產生RedirectView的話,控制器不可避免地要知道某個請求的結果是讓使用者重新導向到另一個頁面。這不是最佳的實現,因為這使得系統不同模組之間結合得過於緊密。其實控制器不應該過問返回結果是怎麼產生的,通常情況下,它應該只關心提供給它的視圖名。
解決上述問題的方法是依靠redirect:首碼。如果返回的視圖名包含redirect:首碼,UrlBasedViewResolver (以及它的子類) 會知道系統要產生一個HTTP redirect。 視圖名其餘的部分會被當作重新導向URL。
這樣做的最終結果跟控制器返回RedirectView是一樣的,但現在控制器只需要和邏輯上的視圖名打交道。 redirect:/my/response/controller.html這個邏輯視圖名中的URL是當前servlet context中的相對路徑。與之相比,redirect:http://myhost.com/some/arbitrary/path.html中的URL是絕對路徑。 重要的是,只要這個重新導向視圖名和其他視圖名以相同的方式注射到控制器中,控制器根本不知道重新導向是否發生了。
forward:首碼
類似的,我們也可以使用包含有forward:首碼的視圖名。這些視圖名會被UrlBasedViewResolver和它的子類正確解析。解析的內部實現是產生一個InternalResourceView,這個視圖最終會調用RequestDispatcher.forward()方法,將forward視圖名的其餘部分作為URL。所以,當你使用InternalResourceViewResolver/InternalResourceView,並且你所用的視圖技術是JSP時,你沒有必要使用這個首碼。但是,當你主要使用其它的視圖技術,但仍需要對Servlet/JSP engine處理的頁面強制forward時,這個forward首碼還是很有用的(但就這個問題而言,如果你不想用forward首碼,你也可以使用視圖解析鏈)。
和redirect:首碼一樣,如果含有forward首碼的視圖名和其他視圖名一樣被注入控制器,控制器根本不知道forward是否發生了。