• Servlet请求转发与重定向


    目录

    一、请求转发

    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 的类,代码如下:

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

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

      "
      );
    20. // 向 request 域对象中添加属性,传递给下一个 Web 资源.
    21. request.setAttribute("属性名1", "属性值1");
    22. request.setAttribute("属性名2", "属性值2");
    23. request.setAttribute("属性名3", "属性值3");
    24. // 转发
    25. request.getRequestDispatcher("/DoServlet").forward(request, response);
    26. }
    27. @Override
    28. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    29. doGet(req, resp);
    30. }
    31. }

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

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

      " + value1 + "

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

      " + value2 + "

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

      " + value3 + "

      "
      );
    30. }
    31. // 获取用户名
    32. String username = request.getParameter("username");
    33. // 获取密码
    34. String password = request.getParameter("password");
    35. // 获取性别
    36. String sex = request.getParameter("sex");
    37. // 获取城市
    38. String city = request.getParameter("city");
    39. // 获取使用语言
    40. String[] languages = request.getParameterValues("language");
    41. writer.write("用户名: " + username + "
      "
      + "密码: " + password + "
      "
      + "性别: " + sex + "
      "
      + "城市: " + city
    42. + "
      "
      + "使用过的语言: " + Arrays.toString(languages));
    43. }
    44. protected void doPost(HttpServletRequest request, HttpServletResponse response)
    45. throws ServletException, IOException {
    46. doGet(request, response);
    47. }
    48. }

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

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Insert title heretitle>
    6. head>
    7. <body>
    8. <form action="/servletDemo/RequestDispatcherServlet" method="GET">
    9. <table border="1">
    10. <tr>
    11. <td colspan="2" align="center">CSDNtd>
    12. tr>
    13. <tr>
    14. <td>姓名td>
    15. <td>
    16. <input type="text" name="username" />
    17. td>
    18. tr>
    19. <tr>
    20. <td>密码td>
    21. <td>
    22. <input type="password" name="password" />
    23. td>
    24. tr>
    25. <tr>
    26. <td>性别td>
    27. <td>
    28. <input type="radio" name="sex" value="男" />
    29. <input type="radio" name="sex" value="女" />
    30. td>
    31. tr>
    32. <tr>
    33. <td>使用的语言td>
    34. <td>
    35. <input type="checkbox" name="language" value="JAVA" />JAVA
    36. <input type="checkbox" name="language" value="C语言" />C语言
    37. <input type="checkbox" name="language" value="PHP" />PHP
    38. <input type="checkbox" name="language" value="Python" />Python
    39. td>
    40. tr>
    41. <tr>
    42. <td>城市td>
    43. <td>
    44. <select name="city">
    45. <option value="none">--请选择--option>
    46. <option value="北京">北京option>
    47. <option value="上海">上海option>
    48. <option value="广州">广州option>
    49. select>
    50. td>
    51. tr>
    52. <tr>
    53. <td colspan="2" align="center" >
    54. <input type="submit" value="提交" />
    55. td>
    56. tr>
    57. table>
    58. form>
    59. body>
    60. 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,代码如下:

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Insert title heretitle>
    6. head>
    7. <body>
    8. <form action="/servletDemo/ReDirectServlet" method="GET">
    9. <table border="1">
    10. <tr>
    11. <td colspan="2" align="center">CSDNtd>
    12. tr>
    13. <tr>
    14. <td>姓名td>
    15. <td>
    16. <input type="text" name="username" />
    17. td>
    18. tr>
    19. <tr>
    20. <td>密码td>
    21. <td>
    22. <input type="password" name="password" />
    23. td>
    24. tr>
    25. <tr>
    26. <td>性别td>
    27. <td>
    28. <input type="radio" name="sex" value="男" />
    29. <input type="radio" name="sex" value="女" />
    30. td>
    31. tr>
    32. <tr>
    33. <td>使用的语言td>
    34. <td>
    35. <input type="checkbox" name="language" value="JAVA" />JAVA
    36. <input type="checkbox" name="language" value="C语言" />C语言
    37. <input type="checkbox" name="language" value="PHP" />PHP
    38. <input type="checkbox" name="language" value="Python" />Python
    39. td>
    40. tr>
    41. <tr>
    42. <td>城市td>
    43. <td>
    44. <select name="city">
    45. <option value="none">--请选择--option>
    46. <option value="北京">北京option>
    47. <option value="上海">上海option>
    48. <option value="广州">广州option>
    49. select>
    50. td>
    51. tr>
    52. <tr>
    53. <td>验证码td>
    54. <td><input type="text" name="code"/>
    55. <img id="imgId" src="/servletDemo/CheckCodeServlet" />
    56. <a href="#" onclick="run()">看不清,换一张a>
    57. td>
    58. tr>
    59. <tr>
    60. <td colspan="2" align="center" >
    61. <input type="submit" value="提交" />
    62. td>
    63. tr>
    64. table>
    65. form>
    66. body>
    67. <script type="text/javascript">
    68. // 看不清,换一张,时间戳
    69. function run() {
    70. // 获取图片
    71. var image = document.getElementById("imgId");
    72. image.src = "/servletDemo/CheckCodeServlet?" + new Date().getTime();
    73. }
    74. script>
    75. html>

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

    1. package com.hoperun.www;
    2. import java.awt.Color;
    3. import java.awt.Font;
    4. import java.awt.Graphics2D;
    5. import java.awt.image.BufferedImage;
    6. import java.io.IOException;
    7. import java.util.ArrayList;
    8. import java.util.List;
    9. import java.util.Random;
    10. import javax.imageio.ImageIO;
    11. import jakarta.servlet.ServletException;
    12. import jakarta.servlet.annotation.WebServlet;
    13. import jakarta.servlet.http.HttpServlet;
    14. import jakarta.servlet.http.HttpServletRequest;
    15. import jakarta.servlet.http.HttpServletResponse;
    16. /**
    17. * 使用 Java 生成验证码图片,并通过 response 对象展示在页面上.
    18. */
    19. @WebServlet("/CheckCodeServlet")
    20. public class CheckCodeServlet extends HttpServlet {
    21. private static final long serialVersionUID = 1L;
    22. @Override
    23. protected void doGet(HttpServletRequest request, HttpServletResponse response)
    24. throws ServletException, IOException {
    25. int width = 120;
    26. int height = 30;
    27. // 在内存中生成图片
    28. BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    29. // 先获取画笔对象
    30. Graphics2D g = (Graphics2D) image.getGraphics();
    31. // 设置灰色
    32. g.setColor(Color.GRAY);
    33. // 画填充的矩形
    34. g.fillRect(0, 0, width, height);
    35. // 设置颜色
    36. g.setColor(Color.BLUE);
    37. // 画边框
    38. g.drawRect(0, 0, width - 1, height - 1);
    39. // 设置颜色
    40. g.setColor(Color.YELLOW);
    41. // 设置字体
    42. g.setFont(new Font("隶书", Font.BOLD, 20));
    43. // 准备数据,随机获取4个字符
    44. String words = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    45. String code = "";
    46. // 构造存储字符串的集合
    47. List list = new ArrayList();
    48. Random random = new Random();
    49. int x = 20;
    50. int y = 20;
    51. for (int i = 0; i < 4; i++) {
    52. // 获取正负30之间的角度
    53. int jiaodu = random.nextInt(60) - 30;
    54. double hudu = jiaodu * Math.PI / 180;
    55. g.rotate(hudu, x, y);
    56. // 获取下标
    57. int index = random.nextInt(words.length());
    58. // 返回指定下标位置的字符,随机获取下标
    59. char ch = words.charAt(index);
    60. // 将字符存入字符数组中
    61. char[] chc = { ch };
    62. // 使用字符数组构造字符串
    63. String string = new String(chc);
    64. // 将构造好的字符串添加进list集合中
    65. list.add(string);
    66. // 写字符串
    67. g.drawString("" + ch, x, y);
    68. g.rotate(-hudu, x, y);
    69. x += 20;
    70. }
    71. for (int i = 0; i < list.size(); i++) {
    72. code += list.get(i);
    73. }
    74. // 将验证码存入上下文中
    75. getServletContext().setAttribute("code", code);
    76. // 设置颜色
    77. g.setColor(Color.GREEN);
    78. int x1, x2, y1, y2;
    79. // 画干扰线
    80. for (int i = 0; i < 4; i++) {
    81. x1 = random.nextInt(width);
    82. y1 = random.nextInt(height);
    83. x2 = random.nextInt(width);
    84. y2 = random.nextInt(height);
    85. g.drawLine(x1, y1, x2, y2);
    86. }
    87. // 输出到客户端
    88. ImageIO.write(image, "jpg", response.getOutputStream());
    89. }
    90. @Override
    91. protected void doPost(HttpServletRequest request, HttpServletResponse response)
    92. throws ServletException, IOException {
    93. doGet(request, response);
    94. }
    95. }

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

    1. package com.hoperun.www;
    2. import java.io.IOException;
    3. import jakarta.servlet.ServletException;
    4. import jakarta.servlet.annotation.WebServlet;
    5. import jakarta.servlet.http.HttpServlet;
    6. import jakarta.servlet.http.HttpServletRequest;
    7. import jakarta.servlet.http.HttpServletResponse;
    8. /**
    9. * 验证提交的信息
    10. */
    11. @WebServlet("/ReDirectServlet")
    12. public class ReDirectServletDemo extends HttpServlet {
    13. private static final long serialVersionUID = 1L;
    14. protected void doGet(HttpServletRequest request, HttpServletResponse response)
    15. throws ServletException, IOException {
    16. // 获取用户名
    17. String username = request.getParameter("username");
    18. // 获取密码
    19. String password = request.getParameter("password");
    20. // 获取验证码
    21. String code = request.getParameter("code");
    22. // 设置是否成功标识
    23. Boolean IsSuccess = true;
    24. // 从上下文获取存储的验证码
    25. String code1 = (String) getServletContext().getAttribute("code");
    26. // 账号密码为admin.且验证码(忽略大小写)输入正确,则跳转到登陆成功页面
    27. if (code != null && !code.isEmpty() && code.equalsIgnoreCase(code1) && "admin".equals(username)
    28. && "admin".equals(password)) {
    29. response.sendRedirect("/servletDemo/SuccessServlet");
    30. // 账号密码不为admin,设置错误信息
    31. } else if (!"admin".equals(username) || !"admin".equals(password)) {
    32. getServletContext().setAttribute("msg", "账号或密码不正确!");
    33. IsSuccess = false;
    34. // 验证码错误,设置错误信息
    35. } else if (code == null || code.isEmpty() || !code.equalsIgnoreCase(code1)) {
    36. getServletContext().setAttribute("msg", "验证码输入错误!");
    37. IsSuccess = false;
    38. }
    39. if (!IsSuccess) {
    40. // 设置自动跳转的时间,存储在上下文中
    41. getServletContext().setAttribute("time", 5);
    42. // 向request对象中设置属性requestAttr,在重定向之后取值。
    43. request.setAttribute("requestAttr", "重定向中使用request域对象传递的数据");
    44. response.sendRedirect("/servletDemo/RefreshServlet");
    45. }
    46. }
    47. protected void doPost(HttpServletRequest request, HttpServletResponse response)
    48. throws ServletException, IOException {
    49. doGet(request, response);
    50. }
    51. }

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

    1. package com.hoperun.www;
    2. import java.io.IOException;
    3. import java.text.SimpleDateFormat;
    4. import java.util.Calendar;
    5. import java.util.Date;
    6. import jakarta.servlet.ServletException;
    7. import jakarta.servlet.annotation.WebServlet;
    8. import jakarta.servlet.http.HttpServlet;
    9. import jakarta.servlet.http.HttpServletRequest;
    10. import jakarta.servlet.http.HttpServletResponse;
    11. /**
    12. * 登录失败后,提示错误信息并定时跳转回登录页面. 通过设置响应头字段(refresh)实现页面的定时跳转
    13. */
    14. @WebServlet("/RefreshServlet")
    15. public class RefreshServletDemo extends HttpServlet {
    16. private static final long serialVersionUID = 1L;
    17. protected void doGet(HttpServletRequest request, HttpServletResponse response)
    18. throws ServletException, IOException {
    19. Object requestAttr = request.getAttribute("requestAttr");
    20. // 获取存在上下文中的跳转时间
    21. int times = (int) getServletContext().getAttribute("time");
    22. // 获取存在上下文中的错误信息
    23. String msg = (String) getServletContext().getAttribute("msg");
    24. /**
    25. * 设置三个头信息,禁用浏览器缓存 Expires: -1 值是日期类型 Pragma : no-cache
    26. */
    27. // 设置三个头信息:
    28. // 设置禁用浏览器缓存
    29. response.setHeader("Cache-Control", "no-cache");
    30. response.setHeader("Pragma", "no-cache");
    31. // 这个头信息指定内容过期的时间,在这之后内容不再被缓存。-1:立即过期
    32. response.setDateHeader("Expires", -1);
    33. // 设置向页面输出的格式
    34. response.setContentType("text/html;charset=UTF-8");
    35. // 设置提示
    36. String title = msg + ",即将在" + times + "秒钟后跳转到登录页";
    37. // 使用默认时区和语言环境获得一个日历
    38. Calendar cale = Calendar.getInstance();
    39. // 将Calendar类型转换成Date类型
    40. Date tasktime = cale.getTime();
    41. // 设置日期输出的格式
    42. SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    43. // 格式化输出
    44. String nowTime = df.format(tasktime);
    45. // 只要倒计时时间没有到0,就直至输出下面内容
    46. if (times != 0) {
    47. response.getWriter()
    48. .write("\n" + ""</span> + title + <span class="hljs-string">"\n" + "\n"
    49. + "

      CSDN 提醒您:

      "
    50. + "

      " + title + "

      \n"
    51. + "

      当前时间是: " + nowTime + "

      \n"
    52. + "

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

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

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

    1. package com.hoperun.www;
    2. import java.io.IOException;
    3. import java.io.PrintWriter;
    4. import jakarta.servlet.ServletException;
    5. import jakarta.servlet.annotation.WebServlet;
    6. import jakarta.servlet.http.HttpServlet;
    7. import jakarta.servlet.http.HttpServletRequest;
    8. import jakarta.servlet.http.HttpServletResponse;
    9. /**
    10. * 登录成功
    11. */
    12. @WebServlet("/SuccessServlet")
    13. public class SuccessServletDemo extends HttpServlet {
    14. private static final long serialVersionUID = 1L;
    15. protected void doGet(HttpServletRequest request, HttpServletResponse response)
    16. throws ServletException, IOException {
    17. // 设置响应输出的格式
    18. response.setContentType("text/html;charset=UTF-8");
    19. PrintWriter writer = response.getWriter();
    20. writer.write("

      登录成功

      "
      );
    21. }
    22. protected void doPost(HttpServletRequest request, HttpServletResponse response)
    23. throws ServletException, IOException {
    24. doGet(request, response);
    25. }
    26. }

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

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

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

    3、转发和重定向的区别

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

    区别转发重定向
    浏览器地址栏 URL 是否发生改变
    是否支持跨域跳转
    请求与响应的次数一次请求和一次响应两次请求和两次响应
    是否共享 request 对象和 response 对象    
    是否能通过 request 域对象传递数据
    速度相对要快相对要慢
    行为类型服务器行为客户端行为
  • 相关阅读:
    Linux 指令学习
    如何在手机上设置节日提醒和倒计时天数?
    华山西安三日行
    Threejs_02 父子位移+缩放改变
    macos 不支持svn安装
    py12_[接口全网最通俗易懂的一篇] Python 真假接口/抽象类/抽象方法/多态/实例级别解析
    跟我学UDS(ISO14229) ———— 0x29(Authentication)
    SpringSecurity+ Oauth2.0+JWT 0-1
    Docker 安装 Redis
    Zookeeper原理讲解(面试题)
  • 原文地址:https://blog.csdn.net/weixin_42051619/article/details/128068221