知識庫 / Spring / Spring MVC RSS 訂閱

Spring 轉向指南

Spring MVC
HongKong
5
02:53 PM · Dec 06 ,2025

1. 概述

本教程將重點介紹在 Spring 中實現重定向的方法,並討論每種策略背後的原因。

2. 為什麼要進行重定向?

首先,我們來考慮一下在 Spring 應用中進行重定向的原因。

當然,可能存在許多例子和原因。例如,我們可能需要處理 POST 表單數據、規避雙重提交問題,或者將執行流程委派給另一個控制器方法。

這裏做一個簡短的説明:典型的 Post/Redirect/Get 模式並不能充分解決雙重提交問題,刷新頁面之前初始提交未完成的情況仍然可能導致雙重提交。

3. 使用 RedirectView 進行重定向

讓我們從這種簡單方法開始,直接跳轉到示例

@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,它將被暴露為 HTTP 查詢參數。模型必須只包含對象——通常是字符串或可以轉換為字符串的對象。

現在,讓我們使用簡單的 curl 命令測試我們的重定向

curl -i http://localhost:8080/spring-rest/redirectWithRedirectView

以下是我們的結果:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: 
  http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView

使用前綴 <em redirect:> 進行重定向

之前的做法——使用 <em RedirectView> ——對於幾個原因來説是不理想的。

首先,我們現在與 Spring API 耦合,因為我們在代碼中直接使用了 <em RedirectView>。

其次,我們現在需要從一開始就知道,在實現該控制器操作時,結果總是會是一個重定向,而這並不總是真的。

更好的選擇是使用前綴 <em redirect:>。重定向視圖名稱像任何其他邏輯視圖名稱一樣注入到控制器中。 控制器根本不瞭解重定向正在發生

下面是如何實現的:

@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-and-forward/redirectedUrl 這樣的名稱。

因此,當我們執行 curl 命令時:

curl -i http://localhost:8080/spring-rest/redirectWithRedirectPrefix

我們將會立即被重定向:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: 
  http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectPrefix

5. 使用前綴轉發 轉發時,使用:

現在我們來看如何做一些稍微不同的事情:轉發。

在代碼之前,我們先回顧一下 轉發與重定向語義的快速、高層次概述

  • 重定向 會使用 302 狀態碼,並將新的 URL 放入 Location 頭部;瀏覽器/客户端會向新的 URL 發出另一個請求。
  • 轉發 完全在服務器端進行。Servlet 容器會將相同的請求轉發到目標 URL;瀏覽器中的 URL 不會發生變化。

現在讓我們來看一下代碼:

@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 執行命令時:

curl -I http://localhost:8080/spring-rest/forwardWithForwardPrefix

我們將會收到 HTTP 405 (方法不允許):

HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
Allow: GET
Content-Type: text/html;charset=utf-8

總結一下,與重定向解決方案中遇到的兩個請求相比,在本例中我們只有一個請求從瀏覽器/客户端發出到服務器端。此前重定向添加的屬性,當然也已消失。

6. 使用 RedirectAttributes 屬性

接下來,讓我們更詳細地瞭解在重定向中傳遞屬性,充分利用框架中的 RedirectAttributes

@GetMapping("/redirectWithRedirectAttributes")
public RedirectView redirectWithRedirectAttributes(RedirectAttributes attributes) {
    attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectAttributes");
    attributes.addAttribute("attribute", "redirectWithRedirectAttributes");
    return new RedirectView("redirectedUrl");
}

正如我們之前所見,我們可以直接在方法中注入 attributes 對象,這使得這個機制非常易於使用。

請注意,我們還添加了 flash 屬性。 這是一個不會進入 URL 的屬性。

通過這種屬性,我們可以稍後僅在最終重定向的目標方法中,使用 @ModelAttribute(“flashAttribute”) 訪問 flash 屬性。

@GetMapping("/redirectedUrl")
public ModelAndView redirection(
  ModelMap model, 
  @ModelAttribute("flashAttribute") Object flashAttribute) {
     model.addAttribute("redirectionAttribute", flashAttribute);
     return new ModelAndView("redirection", model);
 }

因此,總結一下,如果我們使用 curl 測試功能:

curl -i http://localhost:8080/spring-rest/redirectWithRedirectAttributes

我們將被重定向到新的位置:

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

這樣一來,使用 RedirectAttributes 代替 ModelMap 能夠讓我們僅在重定向操作中共享 某些屬性,這兩種方法之間。

7. 不使用前綴的替代配置

現在,讓我們探索一種替代配置:在不使用前綴的情況下進行重定向。

要實現這一點,我們需要使用一個 org.springframework.web.servlet.view.XmlViewResolver

<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

<bean 
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
</bean>

我們還需要在配置中定義一個 RedirectView Bean:

<bean id="RedirectedUrl" class="org.springframework.web.servlet.view.RedirectView">
    <property name="url" value="redirectedUrl" />
</bean>

現在我們可以通過引用這個新的 Bean 來按 ID 觸發重定向:

@Controller
@RequestMapping("/")
public class RedirectController {
    
    @GetMapping("/redirectWithXMLConfig")
    public ModelAndView redirectWithUsingXMLConfig(ModelMap model) {
        model.addAttribute("attribute", "redirectWithXMLConfig");
        return new ModelAndView("RedirectedUrl", model);
    }
}

為了測試它,我們將再次使用 curl 命令

curl -i http://localhost:8080/spring-rest/redirectWithRedirectView

以下是我們得到的結果:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: 
  http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView

8. 重定向 HTTP POST 請求

對於像銀行支付等用例,我們可能需要重定向一個 HTTP POST 請求。根據返回的 HTTP 狀態碼,POST 請求可以重定向到 HTTP GET 或 POST。

根據 HTTP 1.1 協議 參考,狀態碼 301 (永久移動) 和 302 (找到) 允許請求方法從 POST 更改為 GET。該規範還定義了狀態碼 307 (臨時重定向) 和 308 (永久重定向),這些狀態碼不允許請求方法從 POST 更改為 GET。

下面是重定向 POST 請求到另一個 POST 請求的代碼示例:

@PostMapping("/redirectPostToPost")
public ModelAndView redirectPostToPost(HttpServletRequest request) {
    request.setAttribute(
      View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
    return new ModelAndView("redirect:/redirectedPostToPost");
}
@PostMapping("/redirectedPostToPost")
public ModelAndView redirectedPostToPost() {
    return new ModelAndView("redirection");
}

現在我們將使用 curl 命令測試 POST 重定向:

curl -L --verbose -X POST http://localhost:8080/spring-rest/redirectPostToPost

我們被重定向到目標位置:

> 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. 使用參數轉發

現在,讓我們考慮一個場景,即我們希望將參數傳遞給另一個帶有 forward 前綴的 RequestMapping

在這種情況下,我們可以使用 HttpServletRequest 在調用之間傳遞參數。

以下是一個需要將 param1param2 發送到另一個映射 forwardedWithParams 的方法:

@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
public ModelAndView forwardWithParams(HttpServletRequest request) {
    request.setAttribute("param1", "one");
    request.setAttribute("param2", "two");
    return new ModelAndView("forward:/forwardedWithParams");
}

事實上,forwardedWithParams映射可以在完全獨立的控制器中存在,也不需要位於同一個控制器中。

@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
@Controller
@RequestMapping("/")
public class RedirectParamController {

    @RequestMapping(value = "/forwardedWithParams", method = RequestMethod.GET)
    public RedirectView forwardedWithParams(
      final RedirectAttributes redirectAttributes, HttpServletRequest request) {
        redirectAttributes.addAttribute("param1", request.getAttribute("param1"));
        redirectAttributes.addAttribute("param2", request.getAttribute("param2"));

        redirectAttributes.addAttribute("attribute", "forwardedWithParams");
        return new RedirectView("redirectedUrl");
    }
}

為了説明,我們嘗試以下 curl 命令:

curl -i http://localhost:8080/spring-rest/forwardWithParams

以下是結果:

HTTP/1.1 302 Found
Date: Fri, 19 Feb 2021 05:37:14 GMT
Content-Language: en-IN
Location: http://localhost:8080/spring-rest/redirectedUrl?param1=one&param2=two&attribute=forwardedWithParams
Content-Length: 0

如我們所見,param1param2 從第一個控制器傳遞到第二個控制器。最終,它們出現在名為 redirectedUrl 的重定向中,而 forwardedWithParams 指向該重定向。

10. 結論

本文闡述了在 Spring 中實現重定向的三種不同方法,介紹瞭如何處理和傳遞屬性,以及如何處理 HTTP POST 請求的重定向。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.