# 重定向和转发使用

在 Spring MVC 中，**重定向（Redirect）**和**转发（Forward）**是两种常见的请求处理方式，它们分别用于不同的场景。Spring 提供了灵活的 API 来实现这两种请求方式。

### 1. **转发（Forward）**
**转发**是指服务器将请求转发到另一个资源（如 JSP 页面或另一个控制器方法），并且请求和响应都不会发生改变。即，URL 不会发生变化，客户端仍然看到原始的 URL。

#### 转发的实现
在 Spring MVC 中，可以通过以下两种方式实现转发：

+ 使用 `RequestDispatcher.forward()` 方法。
+ 使用 `ModelAndView` 中的 `"forward:"` 前缀来指定转发的路径。

##### 示例 1：使用 `RequestDispatcher` 转发
```java
@Controller
public class ForwardController {

    @RequestMapping("/forwardExample")
    public void forward(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 使用 Servlet 的 RequestDispatcher 进行转发
        request.getRequestDispatcher("/forwardedPage").forward(request, response);
    }

    @RequestMapping("/forwardedPage")
    public String forwardedPage() {
        return "forwardedPage";  // 返回视图
    }
}
```

##### 示例 2：使用 `ModelAndView` 转发
```java
@Controller
public class ForwardController {

    @RequestMapping("/forwardExample")
    public ModelAndView forwardExample() {
        // 使用 ModelAndView 进行转发
        return new ModelAndView("forward:/forwardedPage");
    }

    @RequestMapping("/forwardedPage")
    public String forwardedPage() {
        return "forwardedPage";  // 返回视图
    }
}
```

**解释：**

+ `forward:/forwardedPage` 表示将请求转发到 `/forwardedPage`，且客户端的 URL 不会发生变化。
+ 在转发的过程中，控制器方法返回的视图会直接被渲染。

#### 转发的特点
+ **URL 不改变**：用户的浏览器地址栏不会发生变化，仍然显示原始的请求路径。
+ **共享同一个请求**：请求数据（如请求参数）会被转发到目标资源，可以通过 `request` 对象共享。
+ **适用于内部请求**：转发是服务端内部的操作，适用于控制器之间的跳转或者从控制器到视图的跳转。

### 2. **重定向（Redirect）**
**重定向**是指服务器告诉客户端（浏览器）重新发起一个新的请求。重定向会导致浏览器地址栏的 URL 更新为新的地址，因此会导致一次新的 HTTP 请求。

#### 重定向的实现
在 Spring MVC 中，重定向可以通过以下几种方式实现：

+ 使用 `redirect:` 前缀返回视图。
+ 使用 `HttpServletResponse.sendRedirect()` 方法。

##### 示例 1：使用 `redirect:` 前缀
```java
@Controller
public class RedirectController {

    @RequestMapping("/redirectExample")
    public String redirectExample() {
        // 使用 redirect: 前缀进行重定向
        return "redirect:/redirectedPage";
    }

    @RequestMapping("/redirectedPage")
    public String redirectedPage() {
        return "redirectedPage";  // 返回视图
    }
}
```

##### 示例 2：使用 `HttpServletResponse.sendRedirect()`
```java
@Controller
public class RedirectController {

    @RequestMapping("/redirectExample")
    public void redirectExample(HttpServletResponse response) throws IOException {
        // 使用 HttpServletResponse.sendRedirect() 进行重定向
        response.sendRedirect("/redirectedPage");
    }

    @RequestMapping("/redirectedPage")
    public String redirectedPage() {
        return "redirectedPage";  // 返回视图
    }
}
```

**解释：**

+ `redirect:/redirectedPage` 会发出一个 HTTP 302 重定向响应，浏览器会发起一次新的请求到 `/redirectedPage`，并更新地址栏的 URL。
+ 使用 `HttpServletResponse.sendRedirect()` 方法也会发送重定向响应，同样会导致浏览器重新发起新的请求。

#### 重定向的特点
+ **URL 改变**：浏览器的地址栏会更新为重定向的目标 URL。
+ **不同的请求**：重定向会产生一次新的 HTTP 请求，原请求的参数不会自动携带到新请求中，除非在重定向 URL 中显式传递。
+ **适用于跨请求跳转**：重定向适合用于不同请求之间的跳转，特别是当需要在多个请求中传递数据时。

### 3. **重定向与转发的区别**
| 特性 | 重定向（Redirect） | 转发（Forward） |
| --- | --- | --- |
| **URL 变化** | 浏览器地址栏会更新为新的 URL | 浏览器地址栏不会改变，仍然是原始请求 URL |
| **请求次数** | 会产生两次请求：第一次请求重定向，第二次是新的目标请求 | 只会产生一次请求（内部请求） |
| **数据传递** | 重定向后，数据不能自动传递到新请求中，除非通过 URL 参数传递 | 可以直接传递请求数据，如请求参数和请求属性 |
| **适用场景** | 当需要跨请求或跨控制器传递数据时，或者需要跳转到外部 URL | 当请求处理完成后，内部跳转到另一个视图或资源时 |
| **性能** | 因为需要进行两次请求，所以相比转发略有性能开销 | 因为只需要一次请求，性能开销较小 |


### 4. **常见使用场景**
#### (1) **重定向的使用场景**
+ **表单提交后的重定向**：表单数据提交后，为了防止表单重复提交（用户刷新页面时），常常会使用重定向到另一个页面，这种方法叫做 **Post/Redirect/Get (PRG)** 模式。例如，用户提交了一个订单数据后，页面显示 "订单已提交"，并重定向到订单查看页面。

```java
@RequestMapping("/submitOrder")
public String submitOrder(Order order) {
    orderService.save(order);
    // 提交订单后重定向到订单详情页
    return "redirect:/orderDetails?orderId=" + order.getId();
}
```

+ **外部 URL 重定向**：在用户登录后重定向到外部系统或第三方网站。

```java
@RequestMapping("/redirectToExternalSite")
public String redirectToExternal() {
    // 重定向到外部 URL
    return "redirect:http://www.example.com";
}
```

#### (2) **转发的使用场景**
+ **内部资源的转发**：当需要在同一应用程序内部跳转时（如从控制器到视图），或者从一个控制器跳转到另一个控制器时，可以使用转发。例如，在处理用户请求后，将其转发到 JSP 页面进行显示：

```java
@RequestMapping("/showUser")
public String showUserDetails(Model model) {
    User user = userService.getUser();
    model.addAttribute("user", user);
    return "forward:/userDetails.jsp";  // 内部转发到 userDetails.jsp 页面
}
```

+ **从一个控制器跳转到另一个控制器**：在处理完业务逻辑后，可能需要跳转到另一个控制器来处理某些逻辑。

```java
@RequestMapping("/processOrder")
public String processOrder(Order order) {
    orderService.process(order);
    return "forward:/orderConfirmation";  // 内部跳转到订单确认页面
}
```

+ **重定向（Redirect）**：客户端浏览器会发起一次新的请求，地址栏 URL 会发生变化，适用于需要跨请求跳转或外部跳转的场景。
+ **转发（Forward）**：请求在服务器内部被转发，地址栏 URL 不变，适用于同一请求的内部跳转。

## 
