Spring MVC 視圖重定向

2018-07-26 14:12 更新

如前所述,控制器通常都會返回一個邏輯視圖名,然后視圖解析器會把它解析到一個具體的視圖技術(shù)上去渲染。對于一些可以由Servlet或JSP引擎來處理的視圖技術(shù),比如JSP等,這個解析過程通常是由InternalResourceViewResolverInternalResourceView協(xié)作來完成的,而這通常會調(diào)用Servlet的APIRequestDispatcher.forward(..)方法或RequestDispatcher.include(..)方法,并發(fā)生一次內(nèi)部的轉(zhuǎn)發(fā)(forward)或引用(include)。而對于其他的視圖技術(shù),比如Velocity、XSLT等,視圖本身的內(nèi)容是直接被寫回響應(yīng)流中的。

有時,我們想要在視圖渲染之前,先把一個HTTP重定向請求發(fā)送回客戶端。比如,當(dāng)一個控制器成功地接受到了POST過來的數(shù)據(jù),而響應(yīng)僅僅是委托另一個控制器來處理(比如一次成功的表單提交)時,我們希望發(fā)生一次重定向。在這種場景下,如果只是簡單地使用內(nèi)部轉(zhuǎn)發(fā),那么意味著下一個控制器也能看到這次POST請求攜帶的數(shù)據(jù),這可能導(dǎo)致一些潛在的問題,比如可能會與其他期望的數(shù)據(jù)混淆,等。此外,另一種在渲染視圖前對請求進(jìn)行重定向的需求是,防止用戶多次提交表單的數(shù)據(jù)。此時若使用重定向,則瀏覽器會先發(fā)送第一個POST請求;請求被處理后瀏覽器會收到一個重定向響應(yīng),然后瀏覽器直接被重定向到一個不同的URL,最后瀏覽器會使用重定向響應(yīng)中攜帶的URL發(fā)起一次GET請求。因此,從瀏覽器的角度看,當(dāng)前所見的頁面并不是POST請求的結(jié)果,而是一次GET請求的結(jié)果。這就防止了用戶因刷新等原因意外地提交了多次同樣的數(shù)據(jù)。此時刷新會重新GET一次結(jié)果頁,而不是把同樣的POST數(shù)據(jù)再發(fā)送一遍。

重定向視圖 RedirectView

強(qiáng)制重定向的一種方法是,在控制器中創(chuàng)建并返回一個Spring重定向視圖RedirectView的實(shí)例。它會使得DispatcherServlet放棄使用一般的視圖解析機(jī)制,因?yàn)槟阋呀?jīng)返回一個(重定向)視圖給DispatcherServlet了,所以它會構(gòu)造一個視圖來滿足渲染的需求。緊接著RedirectView會調(diào)用HttpServletResponse.sendRedirect()方法,發(fā)送一個HTTP重定向響應(yīng)給客戶端瀏覽器。

如果你決定返回RedirectView,并且這個視圖實(shí)例是由控制器內(nèi)部創(chuàng)建出來的,那我們更推薦在外部配置重定向URL然后注入到控制器中來,而不是寫在控制器里面。這樣它就可以與視圖名一起在配置文件中配置。關(guān)于如何實(shí)現(xiàn)這個解耦,請參考 重定向前綴——redirect:一小節(jié)。

向重定向目標(biāo)傳遞數(shù)據(jù)

模型中的所有屬性默認(rèn)都會考慮作為URI模板變量被添加到重定向URL中。剩下的其他屬性,如果是基本類型或者基本類型的集合或數(shù)組,那它們將被自動添加到URL的查詢參數(shù)中去。如果model是專門為該重定向所準(zhǔn)備的,那么把所有基本類型的屬性添加到查詢參數(shù)中可能是我們期望那個的結(jié)果。但是,在包含注解的控制器中,model可能包含了專門作為渲染用途的屬性(比如一個下拉列表的字段值等)。為了避免把這樣的屬性也暴露在URL中,@RequestMapping方法可以聲明一個RedirectAttributes類型的方法參數(shù),用它來指定專門供重定向視圖RedirectView取用的屬性。如果重定向成功發(fā)生,那么RedirectAttributes對象中的內(nèi)容就會被使用;否則則使用模型model中的數(shù)據(jù)。

RequestMappingHandlerAdapter提供了一個"ignoreDefaultModelOnRedirect"標(biāo)志。它被用來標(biāo)記默認(rèn)Model中的屬性永遠(yuǎn)不應(yīng)該被用于控制器方法的重定向中??刂破鞣椒☉?yīng)該聲明一個RedirectAttributes類的參數(shù)。如果不聲明,那就沒有參數(shù)被傳遞到重定向的視圖RedirectView中。在MVC命名空間或MVC Java編程配置方式中,為了維持向后的兼容性,這個標(biāo)志都仍被保持為false。但如果你的應(yīng)用是一個新的項目,那么我們推薦把它的值設(shè)置成true。

請注意,當(dāng)前請求URI中的模板變量會在填充重定向URL的時候自動對應(yīng)用可見,而不需要顯式地在ModelRedirectAttributes中再添加屬性。請看下面的例子:

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

另外一種向重定向目標(biāo)傳遞數(shù)據(jù)的方法是通過 閃存屬性(Flash Attributes)。與其他重定向?qū)傩圆煌?,flash屬性是存儲在HTTP session中的(因此不會出現(xiàn)在URL中)。更多內(nèi)容,請參考 21.6 使用閃存屬性一節(jié)。

重定向前綴——redirect:

盡管使用RedirectView來做重定向能工作得很好,但如果控制器自身還是需要創(chuàng)建一個RedirectView,那無疑控制器還是了解重定向這么一件事情的發(fā)生。這還是有點(diǎn)不盡完美,不同范疇的耦合還是太強(qiáng)。控制器其實(shí)不應(yīng)該去關(guān)心響應(yīng)會如何被渲染。In general it should operate only in terms of view names that have been injected into it.

一個特別的視圖名前綴能完成這個解耦:redirect:。如果返回的視圖名中含有redirect:前綴,那么UrlBasedViewResolver(及它的所有子類)就會接受到這個信號,意識到這里需要發(fā)生重定向。然后視圖名剩下的部分會被解析成重定向URL。

這種方式與通過控制器返回一個重定向視圖RedirectView所達(dá)到的效果是一樣的,不過這樣一來控制器就可以只專注于處理并返回邏輯視圖名了。如果邏輯視圖名是這樣的形式:redirect:/myapp/some/resource,他們重定向路徑將以Servlet上下文作為相對路徑進(jìn)行查找,而邏輯視圖名如果是這樣的形式:redirect:http://myhost.com/some/arbitrary/path,那么重定向URL使用的就是絕對路徑。

注意的是,如果控制器方法注解了@ResponseStatus,那么注解設(shè)置的狀態(tài)碼值會覆蓋RedirectView設(shè)置的響應(yīng)狀態(tài)碼值。

重定向前綴——forward:

對于最終會被UrlBasedViewResolver或其子類解析的視圖名,你可以使用一個特殊的前綴:forward:。這會導(dǎo)致一個InternalResourceView視圖對象的創(chuàng)建(它最終會調(diào)用RequestDispatcher.forward()方法),后者會認(rèn)為視圖名剩下的部分是一個URL。因此,這個前綴在使用InternalResourceViewResolverInternalResourceView時并沒有特別的作用(比如對于JSP來說)。但當(dāng)你主要使用的是其他的視圖技術(shù),而又想要強(qiáng)制把一個資源轉(zhuǎn)發(fā)給Servlet/JSP引擎進(jìn)行處理時,這個前綴可能就很有用(或者,你也可能同時串聯(lián)多個視圖解析器)。

redirect:前綴一樣,如果控制器中的視圖名使用了forward:前綴,控制器本身并不會發(fā)覺任何異常,它關(guān)注的仍然只是如何處理響應(yīng)的問題。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號