Spring MVC 視圖重定向

2018-07-26 14:12 更新

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

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

重定向視圖 RedirectView

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

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

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

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

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

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

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

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

重定向前綴——redirect:

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

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

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

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

重定向前綴——forward:

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

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


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)