天天看點

視圖重定向0 重定向視圖 RedirectView1 向重定向目标傳遞資料2 重定向字首——redirect:3 重定向字首——forward:

控制器通常都會傳回一個邏輯視圖名,然後視圖解析器會把它解析到一個具體的視圖技術上去渲染。對于一些可以由Servlet或JSP引擎來處理的視圖技術,比如JSP等,這個解析過程通常是由 InternalResourceViewResolver 和 InternalResourceView 協作來完成的,而這通常會調用ServletAPI RequestDispatcher.forward(..) 方法或 RequestDispatcher.include(..) 方法,并發生一次内部的轉發(forward)或引用(include)。而對于其他的視圖技術,比如Velocity、XSLT等,視圖本身的内容是直接被寫回響應流中的。

有時,我們想要在視圖渲染之前,先把一個HTTP重定向請求發送回用戶端。比如,當一個控

制器成功地接受到了 POST 過來的資料,而響應僅僅是委托另一個控制器來處理(比如一次成

功的表單送出)時,我們希望發生一次重定向。在這種場景下,如果隻是簡單地使用内部轉

發,那麼意味着下一個控制器也能看到這次 POST 請求攜帶的資料,這可能導緻一些潛在的問

題,比如可能會與其他期望的資料混淆,等。此外,另一種在渲染視圖前對請求進行重定向

的需求是,防止使用者多次送出表單的資料。此時若使用重定向,則浏覽器會先發送第一

個 POST 請求;請求被處理後浏覽器會收到一個重定向響應,然後浏覽器直接被重定向到一個

不同的URL,最後浏覽器會使用重定向響應中攜帶的URL發起一次 GET 請求。是以,從浏覽

器的角度看,目前所見的頁面并不是 POST 請求的結果,而是一次 GET 請求的結果。這就防

止了使用者因重新整理等原因意外地送出了多次同樣的資料。此時重新整理會重新 GET 一次結果頁,而

不是把同樣的 POST 資料再發送一遍。

0 重定向視圖 RedirectView

強制重定向的一種方法是,在控制器中建立并傳回一個Spring重定向視圖 RedirectView 的執行個體。它會使得 DispatcherServlet 放棄使用一般的視圖解析機制,因為你已經傳回一個(重定向)視圖給 DispatcherServlet 了,是以它會構造一個視圖來滿足渲染的需求。緊接着 RedirectView 會調用 HttpServletResponse.sendRedirect() 方法,發送一個HTTP重定向響應給用戶端浏覽器。

如果你決定傳回 RedirectView ,并且這個視圖執行個體是由控制器内部建立出來的,那更推薦在外部配置重定向URL然後注入到控制器中來,而不是寫在控制器裡面。這樣它就可以與視圖名一起在配置檔案中配置。關于如何實作這個解耦,參考 重定向字首---redirect:

1 向重定向目标傳遞資料

模型中的所有屬性預設都會考慮作為URI模闆變量被添加到重定向URL中。剩下的其他屬性,如果是基本類型或者基本類型的集合或數組,那它們将被自動添加到URL的查詢參數中去。

如果model是專門為該重定向所準備的,那麼把所有基本類型的屬性添加到查詢參數中可能是我們期望的那個結果。但是,在包含注解的控制器中,model可能包含了專門作為渲染用途的屬性(比如一個下拉清單的字段值等)。為了避免把這樣的屬性也暴露在URL中, @RequestMapping 方法可以聲明一個 RedirectAttributes 類型的方法參數,用它來指定專

門供重定向視圖 RedirectView 取用的屬性。如果重定向成功發生,那麼 RedirectAttributes 對象中的内容就會被使用;否則使用模型model中的資料。

RequestMappingHandlerAdapter 提供了一個 "ignoreDefaultModelOnRedirect" 标志。它被用來标記預設 Model 中的屬性永遠不應該被用于控制器方法的重定向中。控制器方法應該聲明一

個 RedirectAttributes 類的參數。如果不聲明,那就沒有參數被傳遞到重定向的視圖 RedirectView 中。在MVC命名空間或MVC Java程式設計配置方式中,為了維持向後的相容性,這個标志都仍被保持為 false 。但如果你的應用是一個新的項目,那麼我們推薦把它的值設定成 true 。

請注意,目前請求URI中的模闆變量會在填充重定向URL的時候自動對應用可見,而不需要顯式地在 Model 或 RedirectAttributes 中再添加屬性。請看下面的例子:

@RequestMapping(path = "/files/{path}", method = RequestMethod.POST)
public String upload(...) {
   // ...
   return "redirect:files/{path}";
}
           

另外一種向重定向目标傳遞資料的方法是通過 閃存屬性(Flash Attributes)。與其他重定向屬性不同,flash屬性是存儲在HTTP session中的(是以不會出現在URL中)。

2 重定向字首——redirect:

盡管使用 RedirectView 來做重定向能工作得很好,但如果控制器自身還需要建立一個 RedirectView ,那無疑控制器還是了解重定向這麼一件事情的發生。這還是有點不盡完美,不同範疇的耦合還是太強。控制器其實不應該去關心響應會如何被渲染。通常,它應該隻關心被注入的視圖的名字。

一個特别的視圖名字首能完成這個解耦: redirect: 。如果傳回的視圖名中含有 redirect: 字首,那麼 UrlBasedViewResolver (及它的所有子類)就會接受到這個信号,意識到這裡需要發生重定向。然後視圖名剩下的部分會被解析成重定向URL。

這種方式與通過控制器傳回一個重定向視圖 RedirectView 所達到的效果是一樣的,不過這樣一來控制器就可以隻專注于處理并傳回邏輯視圖名了。如果邏輯視圖名是這樣的形

式: redirect:/myapp/some/resource ,他們重定向路徑将以Servlet上下文作為相對路徑進行查找,而邏輯視圖名如果是這樣的形式:

redirect:http://myhost.com/some/arbitrary/path

那麼重定向URL使用的就是絕對路徑。

注意的是,如果控制器方法注解了 @ResponseStatus ,那麼注解設定的狀态碼值會覆寫 RedirectView 設定的響應狀态碼值。

3 重定向字首——forward:

對于最終會被 UrlBasedViewResolver 或其子類解析的視圖名,你可以使用一個特殊的字首: forward: 。這會導緻一個 InternalResourceView 視圖對象的建立(它最終會調用 RequestDispatcher.forward() 方法),後者會認為視圖名剩下的部分是一個URL。是以,這個字首在使用 InternalResourceViewResolver 和 InternalResourceView 時并沒有特别的作用(比如對于JSP來說)。但當你主要使用的是其他的視圖技術,而又想要強制把一個資源轉發給Servlet/JSP引擎進行處理時,這個字首可能就很有用(或者,你也可能同時串聯多個視圖解析器)。