(11)转发和重定向的区别及其原理

在Web应用中,资源跳转可以通过请求转发和响应重定向实现。转发是在服务器内部完成,保持同一请求域,适合数据共享;重定向则是浏览器发起新请求,不共享请求域数据,适用于不同请求间的跳转。转发可能导致浏览器刷新时重复操作,而重定向则避免了这个问题。
摘要由CSDN通过智能技术生成

在web应用中完成资源的跳转

在一个web应用中完成资源的跳转可以通过转发或者重定向两种方式, 跳转的资源只要是服务器内部合法的资源即可(如Servlet、JSP、HTML…)

  • 转发机制使用场景: 某个Servlet向request域当中绑定了数据,希望从其他Servlet当中把request域里面的数据取出来
  • 重定向使用场景: 除转发外剩余的所有的请求
方法名功能
request.getRequestDispatcher(“/要转发的路径且不含项目名”).forward(request, response);转发是服务器内部完成资源的跳转(转发路径不加项目名)
response.sendRedirect(request.getContextPath() + “转发的路径”);重定是浏览器发起一次全新的请求(转发路径需要项目名)

请求转发(发送了一次请求)

两个或者多个Servlet共享同一份数据的方式

  • 第一种: 将数据放到ServletContext应用域当中,这种方式由于应用域范围太大占用资源太多不建议使用
  • 第二种请求转发机制(一次请求): 将数据放到request域当中,然后在AServlet中转发到BServlet保证AServlet和BServlet在同一次请求当中

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

  • A转发到B再转发到C,不管转发了多少次都在同一个request当中,因为调用forward方法的时候会将当前的request和response对象传递给下一个Servlet
  • 转发是服务器根据请求路径自己完成内部资源的跳转,转发路径以“/”开始且不加添加项目名 , 跳转动作是Tomcat服务器内部完成的
    在这里插入图片描述

把AServlet和BServlet放到一次请求当中, 即执行了AServlet之后立马跳转到BServlet保证两个Servlet在同一次请求当中

  • 先获取请求转发器对象(包含下一个要跳转资源的路径), 然后调用请求转发器RequestDispatcher的forward方法进行转发(携带request和response参数)
// AServelt向请求域中存储数据并转发到BSevlet
public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取系统当前时间
        Date nowTime = new Date();
        // 将系统当前时间绑定到请求域当中
        request.setAttribute("sysTime", nowTime);
        // 第一步:先获取请求转发器对象,把"/b"这个路径包装到请求转发器当中,实际上是把下一个跳转的资源的路径告知给Tomcat服务器
        //RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
        // 第二步:调用请求转发器RequestDispatcher的forward方法进行转发,转发的时候request和response都是要传递给下一个资源的
        //dispatcher.forward(request, response);

        // 转发到一个Servlet
        request.getRequestDispatcher("/b").forward(request, response);
        // 转发到一个HTML
        // request.getRequestDispatcher("/test.html").forward(request, response);
    }
}

// BSevlet中取出AServelt中向请求域中绑定的数据 
public class BServlet extends HttpServlet{
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException{
		// 可以从request域当中取出绑定的数据
		Object obj = request.getAttribute("sysTime");
        // 输出到浏览器
        response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		out.print("request域当中获取的系统当前时间 = " + obj);
	}
}

响应重定向(发送了多次请求)

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

  • response对象将重定向到的请求路径响应给浏览器 , 浏览器会根据服务器响应的请求路径在地址栏上重新发起一个get请求(重定向路径需要添加项目名)
  • 由于重定向是多次请求所以不能共享请求域中的数据,需要使用其他域如会话域/应用域实现资源共享

在这里插入图片描述

AServlet向请求域中存储数据,在BServlet拿不到AServlet向请求域中存储的数据,因为这是两个不同的请求即不在同一个请求域当中

  • 浏览器一共发送了两次请求:最终浏览器地址栏上显示的地址当然是最后那一次请求的地址,所以重定向会导致浏览器地址栏上的地址发生改变
// AServelt向请求域中存储数据并重定向到BSevlet
public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取系统当前时间
        Date nowTime = new Date();
        // 将系统当前时间绑定到请求域当中
        request.setAttribute("sysTime", nowTime);
        // response对象将请求路径"/servlet/b"响应给浏览器了,浏览器又自发的向服务器发送了一次全新的请求
        // 第一次请求:http://localhost:8080/servlet/a ---> 第二次请求:http://localhost:8080/servlet/b
        response.sendRedirect(request.getContextPath() + "/b");
    }
}

// BSevlet中无法取出AServelt中向请求域中绑定的数据 
public class BServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 从请求域当中取不到存储的数据,取出的是null
		Object obj = request.getAttribute("sysTime");   
        
        // 输出到浏览器
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
		out.print("request域当中获取的系统当前时间 = " + obj);
    }
} 

转发存在浏览器的刷新问题

当使用转发跳转到success.html页面并刷新当前页面时 , 页面刷新一次数据库中会插入一次数据 , 因为地址栏上的请求还是之前的那个请求没变

当使用重定向跳转到success.html页面并刷新当前页面时 , 无论如何刷新数据库中都不会插入数据 , 因为地址栏上的请求变成了最后一次的请求

创建t_student学生表

create table t_student(
    no int,
    name varchar(255),
);

准备Student.html表单页面发起get请求

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>学生</title>
    </head>
    <body>
        <form action="/student/save" method="get">
            学生编号<input type="text" name="no"><br>
            学生姓名<input type="text" name="name"><br>
            <input type="submit" value="保存">
        </form>
    </body>
</html>

编写StudentSaveServlet处理请求保存学生的信息到数据库(配置到web.xml文件中)

 <servlet>
        <servlet-name>student</servlet-name>
        <servlet-class>com.yunqing.oa.test.StudentSaveServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>student</servlet-name>
        <url-pattern>/student/save</url-pattern>
    </servlet-mapping>
public class StudentSaveServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 保存学生信息到数据库
        request.setCharacterEncoding("UTF-8");
        String no = request.getParameter("no");
        String name = request.getParameter("name");
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            conn = DBUtil.getConnection();
            String sql = "insert into t_student(no,name) values(?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1, no);
            ps.setString(2, name);
            count = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn, ps, null);
        }
        // 保存成功之后跳转到成功页面
        if (count == 1) {
            // 转发: 页面刷新一次数据库中会插入一次数据
            //request.getRequestDispatcher("/success.html").forward(request, response);
            // 重定向: 无论如何刷新数据库中都不会插入数据
            response.sendRedirect(request.getContextPath() + "/success.html");
        }else{

        }       
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值