Servlet请求转发与重定向
创始人
2024-02-23 09:26:07
0

目录

一、请求转发

1、RequestDispatcher 接口

2、请求转发的工作原理

3、request 域对象

4、示例

二、重定向

1、response.sendRedirect()

2、示例

3、转发和重定向的区别


一、请求转发

        Web 应用在处理客户端请求时,经常需要多个 Web 资源共同协作才能生成响应结果。但由于 Servlet 对象无法直接调用其他 Servlet 的 service() 方法,所以 Servlet 规范提供了 2 种解决方案:

  • 请求转发
  • 请求包含(了解即可)

        请求转发属于服务器行为。容器接收请求后,Servlet 会先对请求做一些预处理,然后将请求传递给其他 Web 资源,来完成包括生成响应在内的后续工作。

1、RequestDispatcher 接口

        在 jakarta.servlet 包中定义了一个 RequestDispatcher 接口。继承关系如下图所示:

        RequestDispatcher 对象由 Servlet 容器创建,用于封装由路径所标识的 Web 资源。利用 RequestDispatcher 对象可以把请求转发给其他的 Web 资源。

1)Servlet 可以通过以下2种方式获得 RequestDispatcher 对象:

  • 调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径;
  • 调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径;

绝对路径是指:以正斜杠 "/" 开头的路径,"/" 表示当前 Web 应用的根目录。

相对路径是指:相对当前 Web 资源的路径,不以正斜杠 "/" 开头。

2)RequestDispatcher 接口中提供了以下方法:

返回值类型方法描述
voidforward(ServletRequest request, ServletResponse response)用于将请求转发给另一个 Web 资源。该方法必须在响应给客户端之前被调用,否则将抛出 IllegalStateException 异常。
voidinclude(ServletRequest request, ServletResponse response)用于将其他的资源作为当前响应内容包含进来。

2、请求转发的工作原理

        在 Servlet 中,通常使用 forward() 方法将当前请求转发给其他的 Web 资源进行处理。请求转发的工作原理如下图所示:

请求转发具有以下特点:

  • 请求转发不支持跨域访问,只能跳转到当前应用中的资源;
  • 请求转发之后,浏览器地址栏中的 URL 不会发生变化。因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数;
  • 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象;
  • 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端;

3、request 域对象

        request 是 Servlet 的三大域对象之一,它需要与请求转发配合使用,才可以实现动态资源间的数据传递。在 ServletRequest 接口中定义了一系列操作属性的方法,如下表:

返回值类型方法描述
voidsetAttribute(String name, Object o)将 Java 对象与属性名绑定,并将它作为一个属性存放到 request 对象中。参数 name 为属性名,参数 object 为属性值。
ObjectgetAttribute(String name)根据属性名 name,返回 request 中对应的属性值。
voidremoveAttribute(String name)用于移除 request 对象中指定的属性。
EnumerationgetAttributeNames()用于返回 request 对象中的所有属性名的枚举集合。 

4、示例

        创建一个名为 RequestDispatcherServletDemo 的类,代码如下:

package com.hoperun.www;import java.io.IOException;
import java.io.PrintWriter;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;@WebServlet("/RequestDispatcherServlet")
public class RequestDispatcherServletDemo extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 设置向页面输出内容格式response.setContentType("text/html;charset=UTF-8");PrintWriter writer = response.getWriter();// 尝试在请求转发前,向 response 缓冲区写入内容,最后在页面查看是否展示.writer.write("

这是转发前响应信息里的内容。

");// 向 request 域对象中添加属性,传递给下一个 Web 资源.request.setAttribute("属性名1", "属性值1");request.setAttribute("属性名2", "属性值2");request.setAttribute("属性名3", "属性值3");// 转发request.getRequestDispatcher("/DoServlet").forward(request, response);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);} }

        然后,再创建一个名称为 DoServlet 的类,代码如下:

package com.hoperun.www;import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;@WebServlet("/DoServlet")
public class DoServletDemo extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 设置向页面输出内容格式response.setContentType("text/html;charset=UTF-8");PrintWriter writer = response.getWriter();// 获取 request 域对象中的属性值String value1 = (String) request.getAttribute("属性名1");String value2 = (String) request.getAttribute("属性名2");String value3 = (String) request.getAttribute("属性名3");if (value1 != null) {writer.write("

" + value1 + "

");}if (value2 != null) {writer.write("

" + value2 + "

");}if (value3 != null) {writer.write("

" + value3 + "

");}// 获取用户名String username = request.getParameter("username");// 获取密码String password = request.getParameter("password");// 获取性别String sex = request.getParameter("sex");// 获取城市String city = request.getParameter("city");// 获取使用语言String[] languages = request.getParameterValues("language");writer.write("用户名: " + username + "
" + "密码: " + password + "
" + "性别: " + sex + "
" + "城市: " + city+ "
" + "使用过的语言: " + Arrays.toString(languages));}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

在 webapp 根目录下,创建 index.html,代码如下:





Insert title here

CSDN
姓名
密码
性别
使用的语言JAVAC语言PHPPython
城市

        启动 Tomcat 服务器,在地址栏输入 http://localhost:8080/servletDemo/index.html,访问 index.html,结果如下图:

         填写表单信息,点击提交,结果如下图:

二、重定向

        重定向属于客户端行为。服务器在收到客户端请求后,会通知客户端浏览器重新向另外一个 URL 发送请求,这称为请求重定向。它本质上是两次 HTTP 请求,对应两个 request 对象和两个 response 对象。

        重定向的工作流程如下:

  • 用户在浏览器中输入URL请求访问服务器端的 Web 资源;
  • 服务器端的 Web 资源返回一个状态码为 302 的响应。该响应的含义为:通知浏览器再次发送请求,访问另一个 Web 资源(在响应信息中提供了另一个资源的 URL);
  • 当浏览器接收到响应后,立即自动访问另一个指定的 Web 资源;
  • 该 Web 资源将请求处理完成后,由容器把响应信息返回给浏览器进行展示;

1、response.sendRedirect()

        HttpServletResponse 接口中的 sendRedirect() 方法用于实现重定向。

返回值类型方法描述
voidsendRedirect(String location)向浏览器返回状态码为 302 的响应结果,让浏览器访问新的 URL。若指定的 URL 是相对路径,Servlet 容器会将相对路径转换为绝对路径。参数 location 表示重定向的URL。

2、示例

        在 servletDemo 的 webapp 中,创建登录页面 login.html,代码如下:





Insert title here

CSDN
姓名
密码
性别
使用的语言JAVAC语言PHPPython
城市
验证码看不清,换一张

        创建名称为 CheckCodeServlet 的 Servlet 类,代码如下:

package com.hoperun.www;import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;import javax.imageio.ImageIO;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;/*** 使用 Java 生成验证码图片,并通过 response 对象展示在页面上.*/
@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {int width = 120;int height = 30;// 在内存中生成图片BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 先获取画笔对象Graphics2D g = (Graphics2D) image.getGraphics();// 设置灰色g.setColor(Color.GRAY);// 画填充的矩形g.fillRect(0, 0, width, height);// 设置颜色g.setColor(Color.BLUE);// 画边框g.drawRect(0, 0, width - 1, height - 1);// 设置颜色g.setColor(Color.YELLOW);// 设置字体g.setFont(new Font("隶书", Font.BOLD, 20));// 准备数据,随机获取4个字符String words = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";String code = "";// 构造存储字符串的集合List list = new ArrayList();Random random = new Random();int x = 20;int y = 20;for (int i = 0; i < 4; i++) {// 获取正负30之间的角度int jiaodu = random.nextInt(60) - 30;double hudu = jiaodu * Math.PI / 180;g.rotate(hudu, x, y);// 获取下标int index = random.nextInt(words.length());// 返回指定下标位置的字符,随机获取下标char ch = words.charAt(index);// 将字符存入字符数组中char[] chc = { ch };// 使用字符数组构造字符串String string = new String(chc);// 将构造好的字符串添加进list集合中list.add(string);// 写字符串g.drawString("" + ch, x, y);g.rotate(-hudu, x, y);x += 20;}for (int i = 0; i < list.size(); i++) {code += list.get(i);}// 将验证码存入上下文中getServletContext().setAttribute("code", code);// 设置颜色g.setColor(Color.GREEN);int x1, x2, y1, y2;// 画干扰线for (int i = 0; i < 4; i++) {x1 = random.nextInt(width);y1 = random.nextInt(height);x2 = random.nextInt(width);y2 = random.nextInt(height);g.drawLine(x1, y1, x2, y2);}// 输出到客户端ImageIO.write(image, "jpg", response.getOutputStream());}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

        创建名称为 ReDirectServletDemo 的 Servlet 类,代码如下:

package com.hoperun.www;import java.io.IOException;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;/*** 验证提交的信息*/
@WebServlet("/ReDirectServlet")
public class ReDirectServletDemo extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 获取用户名String username = request.getParameter("username");// 获取密码String password = request.getParameter("password");// 获取验证码String code = request.getParameter("code");// 设置是否成功标识Boolean IsSuccess = true;// 从上下文获取存储的验证码String code1 = (String) getServletContext().getAttribute("code");// 账号密码为admin.且验证码(忽略大小写)输入正确,则跳转到登陆成功页面if (code != null && !code.isEmpty() && code.equalsIgnoreCase(code1) && "admin".equals(username)&& "admin".equals(password)) {response.sendRedirect("/servletDemo/SuccessServlet");// 账号密码不为admin,设置错误信息} else if (!"admin".equals(username) || !"admin".equals(password)) {getServletContext().setAttribute("msg", "账号或密码不正确!");IsSuccess = false;// 验证码错误,设置错误信息} else if (code == null || code.isEmpty() || !code.equalsIgnoreCase(code1)) {getServletContext().setAttribute("msg", "验证码输入错误!");IsSuccess = false;}if (!IsSuccess) {// 设置自动跳转的时间,存储在上下文中getServletContext().setAttribute("time", 5);// 向request对象中设置属性requestAttr,在重定向之后取值。request.setAttribute("requestAttr", "重定向中使用request域对象传递的数据");response.sendRedirect("/servletDemo/RefreshServlet");}}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

        创建名称为 RefreshServletDemo 的 Servlet 类,代码如下:

package com.hoperun.www;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;/*** 登录失败后,提示错误信息并定时跳转回登录页面. 通过设置响应头字段(refresh)实现页面的定时跳转*/
@WebServlet("/RefreshServlet")
public class RefreshServletDemo extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {Object requestAttr = request.getAttribute("requestAttr");// 获取存在上下文中的跳转时间int times = (int) getServletContext().getAttribute("time");// 获取存在上下文中的错误信息String msg = (String) getServletContext().getAttribute("msg");/*** 设置三个头信息,禁用浏览器缓存 Expires: -1 值是日期类型 Pragma : no-cache*/// 设置三个头信息:// 设置禁用浏览器缓存response.setHeader("Cache-Control", "no-cache");response.setHeader("Pragma", "no-cache");// 这个头信息指定内容过期的时间,在这之后内容不再被缓存。-1:立即过期response.setDateHeader("Expires", -1);// 设置向页面输出的格式response.setContentType("text/html;charset=UTF-8");// 设置提示String title = msg + ",即将在" + times + "秒钟后跳转到登录页";// 使用默认时区和语言环境获得一个日历Calendar cale = Calendar.getInstance();// 将Calendar类型转换成Date类型Date tasktime = cale.getTime();// 设置日期输出的格式SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化输出String nowTime = df.format(tasktime);// 只要倒计时时间没有到0,就直至输出下面内容if (times != 0) {response.getWriter().write("\n" + "" + title + "\n" + "\n"+ "

CSDN 提醒您:

"+ "

" + title + "

\n"+ "

当前时间是: " + nowTime + "

\n"+ "

重定向通过request传递的数据为: " + requestAttr + "

\n");// 倒计时times--;// 将倒计时的时间重新存入上下文中覆盖原来的值getServletContext().setAttribute("time", times);// 通过refresh头完成页面刷新response.setHeader("refresh", "1;url=/servletDemo/RefreshServlet");} else {// 倒计时归零,则跳转到登陆页面response.sendRedirect("/servletDemo/login.html");}}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

        创建名称为 SuccessServletDemo 的 Servlet 类,代码如下:

package com.hoperun.www;import java.io.IOException;
import java.io.PrintWriter;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;/*** 登录成功*/
@WebServlet("/SuccessServlet")
public class SuccessServletDemo extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 设置响应输出的格式response.setContentType("text/html;charset=UTF-8");PrintWriter writer = response.getWriter();writer.write("

登录成功

");}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

        启动 Tomcat,在地址栏输入http://localhost:8080/servletDemo/login.html,访问登录页,如下图所示:

        在登录页面输入账号、密码、验证码等信息(当账号和密码都为 admin 时验证成功,否则验证失败),这里我们输入错误的验证码点击提交按钮,结果如下图:

         输入正确信息,点击提交,结果如下图:

3、转发和重定向的区别

        转发和重定向都能实现页面的跳转,两者区别如下:

区别转发重定向
浏览器地址栏 URL 是否发生改变
是否支持跨域跳转
请求与响应的次数一次请求和一次响应两次请求和两次响应
是否共享 request 对象和 response 对象    
是否能通过 request 域对象传递数据
速度相对要快相对要慢
行为类型服务器行为客户端行为

相关内容

热门资讯

【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
牛客计算器的改良(Python... 文章目录1.题目描述2.输入描述:3.输出描述:4.示例15.分析6.代码7.结语 链接࿱...
【前端】‘??‘与‘||‘有什... 0 问题 经常写const data = res.data.a ?? ''或者const d...
正大杯|市调大赛|2023备赛... 关键信息 同时随着精细化养宠趋势的深入,宠物消费类目日渐丰富。 本报告通过 Niuco...
文本生成视频Make-A-Vi... Meta公司(原Facebook)在今年9月29日首次推出一款人工智能系...