Skip to content

SpringMVC教程 - 9 转发与重定向

下面介绍一下转发和重定向。

9.1 转发

转发(forward)就是服务器内部的跳转,由服务器自己把请求交给另一个资源处理,特点是:

  • 浏览器 只发起一次请求

  • URL 不改变

  • 请求中的参数和属性 会被保留

  • 属于 服务器内部行为,客户端无法感知。

举个栗子:

java
package com.foooor.hellospringmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.SessionAttributes;

@Controller
public class IndexController {

    @GetMapping("/")
    public String index(Model model) {

        model.addAttribute("message", "For技术栈");  

        return "index";  // 转发到index视图 index.html
    }
}
  • 上面定义了一个接口 / ,方法返回值是 index ,是逻辑视图的名称,然后 DispatcherServlet 将请求转发给视图解析器找到模板,由模板引擎进行渲染,最终生成 HTML并返回给浏览器。所以在 Controller 中方法 return "index"; 这种形式默认就是采用转发的方式跳转的。

我们在 Controller 中还可以将处理转发到另一个接口处理,举个栗子:

java
package com.foooor.hellospringmvc.controller;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Slf4j
@Controller
public class IndexController {

    @GetMapping("/")
    public String index(HttpServletRequest request) {

        log.info("message:{}", request.getAttribute("message"));  // 这里可以获取到 message 属性

        return "index";  // 转发到index视图 index.html
    }

    @GetMapping("/hello")
    public String hello(Model model) {

        // 放入 request 作用域,在 index.html 中可以使用 ${message} 来获取
        model.addAttribute("message", "For技术栈");

        return "forward:/";  // 转发到/接口
    }
}
  • 上面定义了两个接口,/ 接口的返回值是 index ,是表示跳转到 index.html 视图,默认就是转发的方式,底层创建的是 ThymeleafView 对象。
  • /hello 接口的返回值是 forward:/ 表示转发到 / ,表示转发到另一个接口,前面的 forward: 是固定的,底层使用的是 InternalResourceView 对象完成服务器内部的调转。

打开浏览器,访问 http://localhost:8080/hello ,请求会被转发到 http://localhost:8080/ 接口 ,但是浏览器的地址依然是 http://localhost:8080/hello ,而且 request 中的参数依旧被保留,可以在 index.html 中获取到参数。

但实际开发中,这样转发到另一个接口的方式用的不多,而且需要注意,两个接口的请求方法需要一样,上面都是 Get 请求,所以转发没问题,如果将 Get 请求转发到一个 Post 请求的接口会报错

所以转发是一种服务器内部的“跳转”机制,不需要浏览器重新发起请求,转发是属于同一个请求,request、response 还是同一个对象,只是把控制权交给另一个资源处理。


9.2 重定向

重定向(redirect)是服务器告诉浏览器去访问另一个 URL,浏览器会发起新的请求。特点是:

  • 浏览器会 发起第二次请求
  • URL 会改变
  • 请求参数 不会自动保留(可以通过URL追加参数)
  • 属于 客户端行为,浏览器可感知

举个栗子:

在上一个章节的 CRUD 中也进行了使用,通过 /students 跳转到学生列表页面,然后点击新增 /students/add,跳转到编辑页面,点击保存调用 /students/save,服务器保存数据后,应该通过 /students 重新跳回到列表页面,这个时候就使用了重定向,让浏览器重新发起请求跳转到 /students ,两个请求之间没有什么关系。

java
// 跳转到学生列表页
@GetMapping
public String list(Model model) {
    List<Student> studentList = studentService.findAll();
    model.addAttribute("studentList", studentList);  // 将数据传递给页面
    return "student/list";
}

// 保存学生信息(新增或编辑)
@PostMapping("/save")
public String save(Student student) {
    if (student.getId() == null) {
        studentService.save(student); // 新增
    } else {
        studentService.update(student); // 编辑
    }
    return "redirect:/students";  // 这里是重定向
}
  • 重定向必须使用 redirect: 开头,上面 /students/saveredirect:/students 是两个请求,底层创建的视图对象是 RedirectView 对象。
  • 使用重定向(redirect),那么会告诉浏览器重新发起一个请求,所以浏览器发送的是两个请求,会创建新的请求对象。

  1. 为什么保存方法不直接返回 return "student/list"; 跳转(转发)到列表页面?

你从上面的代码也看到在 list() 接口中,是查询了数据以后,才跳转到列表页面的,如果保存后跳转到 list 页面,那么将没有查询数据,页面会显示不出数据。

  1. 那可以使用 return "forward:/students"; 转发到列表接口吗?

理论上是可以的,也能实现功能(但是请求方法需要一致),但是使用转发,在客户端,跳转到列表页面了,浏览器地址显示的是 /students/save 这个就很让人疑惑了。而且此时如果学生刷新页面,浏览器会再次提交表单(地址栏是 /students/save ),造成重复提交。因此,推荐使用重定向进入新的请求流程,重新查询数据并更新页面。


重定向还可以重定向到别的网站,因为涉及到跨域(什么是跨域?),所以需要写全路径,举个栗子:

java
@GetMapping("/hello")
public String hello(Model model) {
    // 重定向到百度
    return "redirect:https://www.baidu.com";  
}

9.3 mvc:view-controller标签

mvc:view-controller 标签是干嘛的?

在 SpringMVC 中,一个常规请求通常需要写一个 Controller 来处理,Controller负责转发到页面,例如:

java
@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

但是像这种没有任何业务逻辑、只是单纯返回一个页面视图接口的 Controller,会让项目中出现很多冗余代码。

为此,Spring 提供了 <mvc:view-controller> 标签,让你不写 Controller 也能完成页面跳转

1 使用方式

在 SpringMVC 的配置文件(如 springmvc-servlet.xml)中添加如下配置:

xml
<!-- 配置登录页面,当用户访问 /login 时,直接返回 login.html -->
<mvc:view-controller path="/login" view-name="login"/>
  • 上面表示当浏览器访问 /login 时,直接转发到逻辑视图 login,不再经过 Controller。SpringMVC 自动为这个路径创建一个“隐式控制器”,直接把请求映射到视图。
  • 如果有多个需求,可以配置多个。

需要注意,要想 <mvc:view-controller> 生效,必须启用 SpringMVC 的注解驱动功能:

xml
<!-- 必须添加如下配置 -->
<mvc:annotation-driven />

这样 SpringMVC 才能识别 <mvc:view-controller> 这个配置。

内容未完......