当前位置: 首页 > news >正文

转发和重定向的区别详解

转发(Forward)和重定向(Redirect)是 Web 开发中两种常用的请求处理方式,主要用于将客户端请求从一个资源转移到另一个资源。它们在实现机制、行为表现和应用场景上有显著区别,以下是对两者的详细解析:


一、转发(Forward)

1.定义

  • 转发是服务器内部的行为,由服务器直接将请求从一个资源(如 Servlet、JSP)传递到另一个资源,客户端(浏览器)对此过程无感知。

  • 地址栏不变用户看到的 URL 是初始请求的地址,而非最终处理请求的资源地址。

2.工作原理

  1. 客户端发送请求到服务器。

  2. 服务器通过 RequestDispatcher 将请求转发到目标资源。

  3. 目标资源处理请求并生成响应。

  4. 服务器将响应返回给客户端。

3.特点

  • 一次请求:客户端仅发起一次请求,服务器内部完成转发。

  • 数据共享:通过 request.setAttribute() 传递数据,目标资源可直接使用。

  • 路径限制:只能转发到同一 Web 应用内的资源。

  • 性能高效:无需额外网络交互。

4.语法

// 在 Servlet 中实现转发
RequestDispatcher dispatcher = request.getRequestDispatcher("/target.jsp");
request.setAttribute("message", "Hello from Forward");
dispatcher.forward(request, response);

5.示例代码验证:

User类

 * 3. 一个JavaBean一般是有规范的:
 *      有无参数的构造方法
 *      属性私有化
 *      对外提供setter和getter方法
 *      重写toString()
 *      重写hashCode + equals
 *      实现java.io.Serializable接口。
 */
public class User implements Serializable {
    private String id;
    private String name;

    public User() {
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) && Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }

    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

AServlet类

package oop1.servlet;


import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.bean.User;

import java.io.IOException;

public class AServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 创建一个用户对象
        User user = new User();
        user.setId("111111");
        user.setName("杰克");

        // 将用户对象存储到请求域当中
        request.setAttribute("userObj", user);

        // 转发
       request.getRequestDispatcher("/b").forward(request, response);

    }
}

BServlet类

package oop1.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class BServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 从请求域当中取出存储的数据
        Object userObj = request.getAttribute("userObj");

        // 输出到浏览器
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.print("请求域当中的用户对象:" + userObj);
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
        <servlet>
               <servlet-name>a</servlet-name>
               <servlet-class>oop1.servlet.AServlet</servlet-class>
           </servlet>
           <servlet-mapping>
               <servlet-name>a</servlet-name>
               <url-pattern>/a</url-pattern>
           </servlet-mapping>
        <servlet>
            <servlet-name>b</servlet-name>
            <servlet-class>oop1.servlet.BServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>b</servlet-name>
            <url-pattern>/b</url-pattern>
        </servlet-mapping>
    

</web-app>

运行结果:

二、重定向(Redirect)

1.定义

  • 重定向是客户端行为,服务器返回一个特殊响应(状态码 302 或 307),指示客户端重新发起对新 URL 的请求。

  • 地址栏变化:用户最终看到的是新请求的 URL。

2.工作原理

  1. 客户端发送请求到服务器。

  2. 服务器返回状态码 302 和 Location 头(新 URL)。

  3. 客户端自动向新 URL 发起第二次请求。

  4. 新资源处理请求并返回响应。

3.特点

  • 两次请求:客户端发起两次独立的请求。

  • 数据隔离:两次请求的 request 对象不同,需通过 URL 参数、Session 或 Cookie 传递数据。

  • 路径灵活:可重定向到任意 URL(包括外部域名)。

  • 性能开销:多一次网络往返,效率略低。

4.语法:

// 在 Servlet 中实现重定向
response.sendRedirect("http://example.com/newPage.jsp");
// 通过 URL 参数传递数据
response.sendRedirect("/newPage.jsp?message=Hello+from+Redirect");

5.示例代码验证

仅修改AServlet类

 // 重定向(重新定方向)


        // 重定向时的路径当中需要以项目名开始,或者说需要添加项目名。
        // response对象将这个路径:"/servlet10/b"响应给浏览器了。
        // 浏览器又自发的向服务器发送了一次全新的请求:http://localhost:8080/servlet10/b
        // 所以浏览器一共发送了两次请求:
        // 第一次请求:http://localhost:8080/servlet10/a
        // 第二次请求:http://localhost:8080/servlet10/b
        // 最终浏览器地址栏上显示的地址当然是最后那一次请求的地址。所以重定向会导致浏览器地址栏上的地址发生改变。

package oop1.servlet;


import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import oop1.bean.User;

import java.io.IOException;

public class AServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 创建一个用户对象
        User user = new User();
        user.setId("111111");
        user.setName("杰克");

        // 将用户对象存储到请求域当中
        request.setAttribute("userObj", user);

    

        // 重定向(重新定方向)
        // 重定向时的路径当中需要以项目名开始,或者说需要添加项目名。
        // response对象将这个路径:"/servlet10/b"响应给浏览器了。
        // 浏览器又自发的向服务器发送了一次全新的请求:http://localhost:8080/servlet10/b
        // 所以浏览器一共发送了两次请求:
        // 第一次请求:http://localhost:8080/servlet10/a
        // 第二次请求:http://localhost:8080/servlet10/b
        // 最终浏览器地址栏上显示的地址当然是最后那一次请求的地址。所以重定向会导致浏览器地址栏上的地址发生改变。
      response.sendRedirect(request.getContextPath() + "/b");
    }
}

运行结果:

三、转发和重定向有什么区别?

1.形式上有什么区别?

  • 转发(一次请求)

    • 在浏览器地址栏上发送的请求是:http://localhost:8080/servlet10/a ,最终请求结束之后,浏览器地址栏上的地址还是这个。没变。

  • 重定向(两次请求)

    • 在浏览器地址栏上发送的请求是:http://localhost:8080/servlet10/a ,最终在浏览器地址栏上显示的地址是:http://localhost:8080/servlet10/b

2.转发和重定向的本质区别?

  • 转发:是由WEB服务器来控制的。A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的。

  • 重定向:是浏览器完成的。具体跳转到哪个资源,是浏览器说了算。

3.转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?

  • 如果在上一个Servlet当中向request域当中绑定了数据,希望从下一个Servlet当中把request域里面的数据取出来,使用转发机制。

  • 剩下所有的请求均使用重定向。(重定向使用较多。)

四、核心区别对比

特性转发(Forward)重定向(Redirect)
请求次数一次请求,服务器内部处理两次独立请求
地址栏变化不变化(显示初始 URL)变化(显示最终 URL)
数据共享通过 request 作用域传递需手动传递(URL、Session 等)
路径范围仅限同一应用内可跨应用、跨域
性能高效(无额外网络交互)较低(多一次往返)
实现方式RequestDispatcher.forward()response.sendRedirect()
HTTP 状态码无明确状态码(服务器内部处理)302(临时)或 307(永久重定向)

五、使用场景

适合转发的场景
  1. 隐藏实现细节:例如表单提交后跳转到结果页,但保持 URL 不变。

  2. 共享请求数据:在多个服务器端资源间传递数据(如 Servlet → JSP)。

  3. 避免重复提交:处理 POST 请求后转发到结果页,防止用户刷新导致重复提交。

适合重定向的场景
  1. 跨应用跳转:例如从旧系统跳转到新系统的页面。

  2. 防止重复提交:处理 POST 请求后重定向到 GET 请求(如 PRG 模式,Post-Redirect-Get)。

  3. 用户登录/注销:登录后重定向到主页,避免刷新时重复提交表单。

  4. 依赖外部资源:例如调用第三方支付接口后重定向回本站。


六、进阶补充

  1. PRG 模式
    通过重定向解决表单重复提交问题:用户提交表单(POST)→ 服务器处理并重定向到结果页(GET)→ 用户刷新不会重复提交数据。

  2. 框架中的使用

    • Spring MVC

      • 转发:return "forward:/target"

      • 重定向:return "redirect:/target"

    • Thymeleaf/JSP:直接通过视图解析器处理转发逻辑。


七、总结

  • 转发:服务器内部跳转,高效但路径受限,适合隐藏实现细节或共享数据。

  • 重定向:客户端跳转,灵活但性能略低,适合跨应用、防重复提交或依赖外部资源。

根据具体需求选择合适的方式,例如对安全性和路径有要求时用转发,需要跨域或避免重复提交时用重定向。

http://www.dtcms.com/a/98830.html

相关文章:

  • Java的string默认值
  • ffuf:一款高效灵活的Web模糊测试利器
  • 右值和右值引用【C++】
  • onlyoffice 多核心研究
  • 763划分字母区间解题记录
  • java基础:常见类和对象
  • 游戏被外挂攻破?金融数据遭篡改?AI反作弊系统实战方案(代码+详细步骤)
  • Linux|gitlab|二进制快速安装部署gitlab-ce教程
  • 19_20 js es6
  • std::countr_zero
  • 模型苏醒计划:Threejs 让静态模型「叛逆」起来
  • Java.util.logging (JUL) 终极指南:从基础配置到高级玩法
  • 外观模式_结构型_GOF23
  • 游戏引擎学习第192天
  • 第三卷:覆舟山决战(73-108回)正反人物群像
  • 习题1.43
  • 软件工程面试题(十一)
  • 【数据结构】队列
  • el-radio-group 中 el-radio-button value未能绑定上数值数据
  • 欢乐力扣:合并两个有序链表
  • Redis6数据结构之List类型
  • 25_闭包节流防抖
  • Gateway实战(三)、断言-时间、Cookie信息
  • 从零开始研发GPS接收机连载——16、接收天上卫星信号成功定位
  • Python之变量与数据类型总结
  • Linux C语言调用第三方库,第三方库如何编译安装
  • Android 12系统源码_输入系统(四)触摸异常问题排查
  • nginx 设置隐藏版本号
  • 【LangChain入门 9 Agent 】LangChain开发Agent智能体
  • 当模板方法模式遇上工厂模式:一道优雅的烹饪架构设计