当前位置:首页 » 《随便一记》 » 正文

Servlet请求转发与重定向

20 人参与  2022年11月28日 15:45  分类 : 《随便一记》  评论

点击全文阅读


目录

一、请求转发

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 对象中指定的属性。
Enumeration<String>getAttributeNames()用于返回 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("<h1>这是转发前响应信息里的内容。</h1>");// 向 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("<h3>" + value1 + "</h3>");}if (value2 != null) {writer.write("<h3>" + value2 + "</h3>");}if (value3 != null) {writer.write("<h3>" + value3 + "</h3>");}// 获取用户名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 + "<br/>" + "密码: " + password + "<br/>" + "性别: " + sex + "<br/>" + "城市: " + city+ "<br/>" + "使用过的语言: " + Arrays.toString(languages));}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

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

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Insert title here</title></head><body>    <form action="/servletDemo/RequestDispatcherServlet" method="GET">        <table border="1">            <tr>                <td colspan="2" align="center">CSDN</td>            </tr>            <tr>                <td>姓名</td>                <td>                <input type="text" name="username" />                </td>            </tr>            <tr>                <td>密码</td>                <td>                <input type="password" name="password" /></td>            </tr>            <tr>                <td>性别</td>                <td>                <input type="radio" name="sex" value="男" />男                <input type="radio" name="sex" value="女" />女</td>            </tr>            <tr>                <td>使用的语言</td>                <td>                <input type="checkbox" name="language" value="JAVA" />JAVA                    <input type="checkbox" name="language" value="C语言" />C语言                    <input type="checkbox" name="language" value="PHP" />PHP                    <input type="checkbox" name="language" value="Python" />Python</td>            </tr>            <tr>                <td>城市</td>                <td>                <select name="city">                        <option value="none">--请选择--</option>                        <option value="北京">北京</option>                        <option value="上海">上海</option>                        <option value="广州">广州</option>                </select></td>            </tr>            <tr>                <td colspan="2" align="center" >                <input type="submit" value="提交" />                </td>            </tr>        </table>    </form></body></html>

        启动 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,代码如下:

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Insert title here</title></head><body><form action="/servletDemo/ReDirectServlet" method="GET">        <table border="1">            <tr>                <td colspan="2" align="center">CSDN</td>            </tr>            <tr>                <td>姓名</td>                <td>                <input type="text" name="username" />                </td>            </tr>            <tr>                <td>密码</td>                <td>                <input type="password" name="password" /></td>            </tr>            <tr>                <td>性别</td>                <td>                <input type="radio" name="sex" value="男" />男                <input type="radio" name="sex" value="女" />女</td>            </tr>            <tr>                <td>使用的语言</td>                <td>                <input type="checkbox" name="language" value="JAVA" />JAVA                    <input type="checkbox" name="language" value="C语言" />C语言                    <input type="checkbox" name="language" value="PHP" />PHP                    <input type="checkbox" name="language" value="Python" />Python</td>            </tr>            <tr>                <td>城市</td>                <td>                <select name="city">                        <option value="none">--请选择--</option>                        <option value="北京">北京</option>                        <option value="上海">上海</option>                        <option value="广州">广州</option>                </select></td>            </tr>            <tr>            <td>验证码</td>            <td><input type="text" name="code"/>                <img id="imgId" src="/servletDemo/CheckCodeServlet" />                <a href="#" onclick="run()">看不清,换一张</a>            </td></tr>            <tr>                <td colspan="2" align="center" >                <input type="submit" value="提交" />                </td>            </tr>        </table>    </form></body><script type="text/javascript">    // 看不清,换一张,时间戳    function run() {        // 获取图片        var image = document.getElementById("imgId");        image.src = "/servletDemo/CheckCodeServlet?" + new Date().getTime();    }</script></html>

        创建名称为 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<String> list = new ArrayList<String>();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("<html>\n" + "<head><title >" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n"+ "<h1 align=\"center\">CSDN  提醒您:</h1>"+ "<h1 align=\"center\" style=\"font-family:arial;color:red;\">" + title + "</h1>\n"+ "<h1 align=\"center\">当前时间是: " + nowTime + "</h1>\n"+ "<h1 align=\"center\">重定向通过request传递的数据为: " + requestAttr + "</h1>\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("<h1>登录成功</h1>");}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 域对象传递数据
速度相对要快相对要慢
行为类型服务器行为客户端行为

点击全文阅读


本文链接:http://zhangshiyu.com/post/49192.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1