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

在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 {@Overrideprotected 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);// 转发到一个Servletrequest.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 {@Overrideprotected 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/bresponse.sendRedirect(request.getContextPath() + "/b");}
}// BSevlet中无法取出AServelt中向请求域中绑定的数据 
public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 从请求域当中取不到存储的数据,取出的是nullObject 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 {@Overrideprotected 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{}       }
}

本文链接:https://my.lmcjl.com/post/2893.html

展开阅读全文

4 评论

留下您的评论.