1. 概述
本文將重點介紹在 Spring 中實現重定向(Redirect),并將討論每個策略背后的原因。
2. 為什么要重定向?
讓我們先來考慮在 Spring 應用程序中為什么您可能需要做一個重定向的原因。
當然有很多可能的例子和原因。 一個簡單的可能是 POST 表單數據,圍繞雙重提交問題,或者只是將執行流委托給另一個控制器方法。
附注一點,典型的 Post / Redirect / Get 模式并不能充分解決雙重提交問題 - 在初始提交完成之前刷新頁面的問題可能仍然會導致雙重提交。
3、使用 RedirectView 重定向
我們從這個簡單的方法開始 - 直接來一個例子:
1
2
3
4
5
6
7
8
9
10
11
|
@Controller @RequestMapping ( "/" ) public class RedirectController { @GetMapping ( "/redirectWithRedirectView" ) public RedirectView redirectWithUsingRedirectView(RedirectAttributes attributes) { attributes.addFlashAttribute( "flashAttribute" , "redirectWithRedirectView" ); attributes.addAttribute( "attribute" , "redirectWithRedirectView" ); return new RedirectView( "redirectedUrl" ); } } |
在背后,RedirectView 會觸發 HttpServletResponse.sendRedirect() - 這將執行實際的重定向。
注意這里我們是如何注入重定向屬性到方法里面的 - 由框架完成這部分繁重的工作,讓我們能夠與這些屬性交互。
我們添加 attribute 到模型RedirectAttributes中 - 將其作為 HTTP 查詢參數(Query parameter)暴露。 該模型包含的對象 - 通常是字符串或可以被轉換成字符串的對象。
現在讓我們來測試我們的重定向功能 - 用一個簡單的 curl 命令來幫助實現:
1
|
curl -i http://localhost:8080/spring-rest/redirectWithRedirectView |
結果將是:
1
2
3
4
|
HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Location: http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView |
4. 使用redirect:前綴進行重定向
前面一個方法使用RedirectView,因為一些原因它并不是最優的。
首先,我們現在是耦合于Spring API的,因為我們在我們的代碼里直接地使用RedirectView。
其次,我們需要從一開始就知道,當實現控制器操作的時候,它的結果將總是重定向的,但情況并非總是如此。
更好的選擇是使用redirect:前綴——重定向視圖名稱像其它邏輯視圖名稱一樣被注入到控制器中。控制器甚至不知道重定向正在發生。
它看起來像是這樣的:
1
2
3
4
5
6
7
8
9
10
|
@Controller @RequestMapping ( "/" ) public class RedirectController { @GetMapping ( "/redirectWithRedirectPrefix" ) public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) { model.addAttribute( "attribute" , "redirectWithRedirectPrefix" ); return new ModelAndView( "redirect:/redirectedUrl" , model); } } |
當視圖名稱跟redirect:一起返回的時候,UrlBasedViewResolver類(以及它的所有子類)會將其識別為一個需要進行重定向的特殊指示。視圖名稱剩下的部分會被當作重定向URL。
這里有一個地方需要注意——當我們在這里使用redirect:/redirectedUrl邏輯視圖的時候,我們正在做一個跟當前Servlet上下文相關的重定向。
如果需要重定向到一個絕對URL,我們可以使用像這樣的名稱:redirect: http://localhost:8080/spring-redirect/redirectedUrl。
所以現在,當我們執行curl命令:
1
|
curl -i http: //localhost :8080 /spring-rest/redirectWithRedirectPrefix |
我們會立刻得到一個重定向:
1
2
3
4
|
HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Location: http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectPrefix |
5. 使用forward前綴轉發:
我們現在看看如何做一些略有不同的事——一個轉發。
在看代碼之前,我們先來看一下對轉發與重定向的語義的快速、高層概括:
- 重定向將以包含302響應碼和Location頭的新URL進行響應;然后瀏覽器/客戶端將再次向新的URL發出請求
- 轉發完全在服務器端發生; Servlet容器將相同的請求轉發到目標URL;瀏覽器中的URL無須改變
現在我們來看看代碼:
1
2
3
4
5
6
7
8
9
10
|
@Controller @RequestMapping ( "/" ) public class RedirectController { @GetMapping ( "/forwardWithForwardPrefix" ) public ModelAndView redirectWithUsingForwardPrefix(ModelMap model) { model.addAttribute( "attribute" , "forwardWithForwardPrefix" ); return new ModelAndView( "forward:/redirectedUrl" , model); } } |
與redirect:一樣,forward:前綴將由UrlBasedViewResolver及其子類解析。在內部,這將創建一個InternalResourceView,它為新視圖執行一個RequestDispatcher.forward()操作。
當我們用curl執行該命令時:
1
|
curl -I http: //localhost :8080 /spring-rest/forwardWithForwardPrefix |
我們會得到HTTP 405 (不允許的方法):
1
2
3
4
|
HTTP/1.1 405 Method Not Allowed Server: Apache-Coyote/1.1 Allow: GET Content-Type: text/html;charset=utf-8 |
與我們在重定向解決方案中的兩個請求相比,在這種情況下,我們只有一個請求從瀏覽器/客戶端發送到服務器端。當然,以前由重定向添加的屬性也不需要了。
6. 包含RedirectAttributes的屬性
接下來 - 讓我們看看在一個重定向中傳遞屬性 - 充分利用框架中的RedirectAttribures:
1
2
3
4
5
6
7
|
@GetMapping ( "/redirectWithRedirectAttributes" ) public RedirectView redirectWithRedirectAttributes(RedirectAttributes attributes) { attributes.addFlashAttribute( "flashAttribute" , "redirectWithRedirectAttributes" ); attributes.addAttribute( "attribute" , "redirectWithRedirectAttributes" ); return new RedirectView( "redirectedUrl" ); } |
如前所述,我們可以直接在方法中插入屬性對象 - 這使得該機制非常容易使用。
還要注意,我們也添加一個Flash屬性 - 這是一個不會被添加到URL中的屬性。我們可以通過這種屬性來實現——我們稍后可以在重定向的最終目標的方法中使用@ModelAttribute(“flashAttribute”)來訪問flash屬性:
1
2
3
4
5
6
7
8
|
@GetMapping ( "/redirectedUrl" ) public ModelAndView redirection( ModelMap model, @ModelAttribute ( "flashAttribute" ) Object flashAttribute) { model.addAttribute( "redirectionAttribute" , flashAttribute); return new ModelAndView( "redirection" , model); } |
因此,圓滿完工——如果你需要使用curl測試該功能:
1
|
curl -i http: //localhost :8080 /spring-rest/redirectWithRedirectAttributes |
我們將會被重定向到新的位置:
1
2
3
4
5
|
HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=4B70D8FADA2FD6C22E73312C2B57E381; Path=/spring-rest/; HttpOnly Location: http://localhost:8080/spring-rest/redirectedUrl; jsessionid=4B70D8FADA2FD6C22E73312C2B57E381?attribute=redirectWithRedirectAttributes |
這樣,使用RedirectAttribures代替ModelMap,賦予我們僅在重定向操作中涉及的兩種方法之間共享一些屬性的能力。
7. 沒有前綴的另一種配置
現在讓我們探索另一種配置——沒有前綴的重定向。
為了實現這一點,我們需要使用org.springframework.web.servlet.view.XmlViewResolver:
1
2
3
4
5
6
|
< bean class = "org.springframework.web.servlet.view.XmlViewResolver" > < property name = "location" > < value >/WEB-INF/spring-views.xml</ value > </ property > < property name = "order" value = "0" /> </ bean > |
代替我們在之前配置里使用的org.springframework.web.servlet.view.InternalResourceViewResolver:
1
2
|
< bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver" > </ bean > |
我們還需要在配置里面定義一個RedirectView bean:
1
2
3
|
< bean id = "RedirectedUrl" class = "org.springframework.web.servlet.view.RedirectView" > < property name = "url" value = "redirectedUrl" /> </ bean > |
現在我們可以通過id來引用這個新的bean來觸發重定向:
1
2
3
4
5
6
7
8
9
10
|
@Controller @RequestMapping ( "/" ) public class RedirectController { @GetMapping ( "/redirectWithXMLConfig" ) public ModelAndView redirectWithUsingXMLConfig(ModelMap model) { model.addAttribute( "attribute" , "redirectWithXMLConfig" ); return new ModelAndView( "RedirectedUrl" , model); } } |
為了測試它,我們再次使用curl命令:
1
|
curl -i http: //localhost :8080 /spring-rest/redirectWithRedirectView |
結果會是:
1
2
3
4
|
HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Location: http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView |
8. 重定向HTTP POST請求 Request
對于類似銀行付款這樣的用例,我們可能需要重定向HTTP POST請求。根據返回的HTTP狀態碼,POST請求可以重定向到HTTP GET或POST上。
根據HTTP 1.1協議參考,狀態碼301(永久移除)和302(已找到)允許請求方法從POST更改為GET。該規范還定義了不允許將請求方法從POST更改為GET的相關的307(臨時重定向)和308(永久重定向)狀態碼。
現在,我們來看看將post請求重定向到另一個post請求的代碼:
1
2
3
4
5
|
@PostMapping ( "/redirectPostToPost" ) public ModelAndView redirectPostToPost(HttpServletRequest request) { request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT); return new ModelAndView( "redirect:/redirectedPostToPost" ); } |
1
2
3
4
|
@PostMapping ( "/redirectedPostToPost" ) public ModelAndView redirectedPostToPost() { return new ModelAndView( "redirection" ); } |
現在,讓我們使用curl命令來測試下重定向的POST:
1
|
curl -L --verbose -X POST http: //localhost :8080 /spring-rest/redirectPostToPost |
我們正在被重定向到目標地址:
1
2
3
4
5
6
7
8
9
10
11
|
> POST /redirectedPostToPost HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.49.0 > Accept: */* > < HTTP/1.1 200 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Tue, 08 Aug 2017 07:33:00 GMT {"id":1,"content":"redirect completed"} |
9. 結論
本文介紹了在Spring中實現重定向的三種不同方法,在執行這些重定向時如何處理/傳遞屬性以及如何處理HTTP POST請求的重定向。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.oschina.net/translate/spring-redirect-and-forward?utm_source=tuicool&utm_medium=referral