0%

HttpServletRequest

  • HttpServletRequest接口详解

2.5 ★★★HttpServletRequest接口详解

  • HttpServletRequest

    1. HttpServletRequest 是什么?

      • jakarta.servlet.http.HttpServletRequest,是 Servlet 规范中的一员。

      • HttpServletRequest 是一个接口,全限定名称为 jakarta.servlet.http.HttpServletRequest

      • HttpServletRequest 接口的父接口是 ServletRequest

        1
        public interface HttpServletRequest extends ServletRequest {}
    2. HttpServletRequest 接口的实现类谁写的? HttpServletRequest 对象是谁给创建的?

      • 通过测试:org.apache.catalina.connector.RequestFacade 实现了 HttpServletRequest 接口:

        1
        public class RequestFacade implements HttpServletRequest {}
      • 测试结果说明:Tomcat 服务器(WEB服务器、WEB容器)实现了 HttpServletRequest 接口,还说明了 Tomcat 服务器实现了 Servlet 规范。

        而对于我们 javaweb 程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可,我们关心的是 HttpServletRequest 接口中有哪些方法,这些方法可以完成什么功能。

    3. HttpServletRequest 对象中都有什么信息?都包装了什么信息?

      • HttpServletRequest 对象是 Tomcat 服务器负责创建的,这个对象中封装了 HTTP 的请求协议
      • 实际上是用户发送请求的时候,遵循了 HTTP 协议,发送的是 HTTP 的请求协议,Tomcat 服务器将 HTTP 协议中的信息以及数据全部解析出来,然后 Tomcat 服务器把这些信息封装到 HttpServletRequest 对象当中,传给了我们 javaweb 程序员。
      • javaweb 程序员面向 HttpServletRequest 接口编程,调用方法就可以获取到请求的信息了。
    4. requestresponse 对象的生命周期?

      • request 对象和 response 对象,一个是请求对象,一个是响应对象,这两个对象只在当前请求中有效
      • 一次请求对应一个 request
      • 两次请求则对应两个 request
    5. HttpServletRequest 怎么获取前端浏览器用户提交的数据

      • 思考:如果是你,前端的 form 表单提交了数据之后,你准备怎么存储这些数据,你准备采用什么样的数据结构去存储这些数据呢?

        • 前端提交的数据格式:username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt

        • 可以采用 Map 集合来存储:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          Map<String,String>
          key存储String
          value存储String
          这种想法对吗?不对。
          如果采用以上的数据结构存储会发现key重复的时候value覆盖。
          key value
          ---------------------
          username abc
          userpwd 111
          aihao s
          aihao d
          aihao tt
          这样是不行的,因为map的key不能重复。
          Map<String, String[]>
          key存储String
          value存储String[]
          key value
          -------------------------------
          username {"abc"}
          userpwd {"111"}
          aihao {"s","d","tt"}

          注意:前端表单提交数据的时候,假设提交了 120 这样的“数字”,其实是以字符串 “120” 的方式提交的,所以服务器端获取到的一定是一个字符串的”120”,而不是一个数字(前端提交的永远是字符串,后端获取的也永远是字符串)。

      • 因此,下面的四个方法就是获得上面的 Map 集合中的数据

        1
        2
        3
        4
        Map<String,String[]> getParameterMap() 获取Map集合
        Enumeration<String> getParameterNames() 获取Map集合中所有的key
        String[] getParameterValues(String name) 根据key获取Map集合的value
        String getParameter(String name) 根据key获取value这个一维数组当中的**第一个元素**。这个方法最常用。
    6. request 对象又称为 “请求域” 对象。

      • “请求域”对象:

        • “请求域” 对象要比 “应用域” 对象范围小很多,生命周期短很多,请求域只在一次请求内有效。

        • 一个请求对象 request 对应一个请求域对象,一次请求结束之后,这个请求域就销毁了。

        • 请求域对象也有这三个方法:

          1
          2
          3
          void setAttribute(String name, Object obj); // 向域当中绑定数据。
          Object getAttribute(String name); // 从域当中根据name获取数据。
          void removeAttribute(String name); // 将域当中绑定的数据移除
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          package com.f.javaweb.servlet;

          import jakarta.servlet.ServletException;
          import jakarta.servlet.http.HttpServlet;
          import jakarta.servlet.http.HttpServletRequest;
          import jakarta.servlet.http.HttpServletResponse;

          import java.io.IOException;
          import java.io.PrintWriter;
          import java.util.Date;

          /**
          * @author fzy
          * @date 2023/11/28 19:26
          */
          public class AServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
          // 向request域中绑定数据
          Date time = new Date();
          request.setAttribute("sysTime", time);
          // 从request域中获取数据
          response.setContentType("text/html");
          PrintWriter out = response.getWriter();
          Date sysTime = (Date) request.getAttribute("sysTime");
          out.print("request域中获取的系统的当前时间: " + sysTime + "<br/>");
          // 移除request域中的数据
          request.removeAttribute("sysTime");
          }
          }
      • “请求域” 和 “应用域” 的选用原则?

        • 尽量使用小的域对象,因为小的域对象占用的资源较少。
    7. ★跳转

      转发(一次请求):在执行了 AServlet 之后,跳转到 BServlet,两个 Servlet 共享同一个 requestresponse 对象。

      • AServlet

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        package com.f.javaweb.servlet;

        import jakarta.servlet.RequestDispatcher;
        import jakarta.servlet.ServletException;
        import jakarta.servlet.http.HttpServlet;
        import jakarta.servlet.http.HttpServletRequest;
        import jakarta.servlet.http.HttpServletResponse;

        import java.io.IOException;
        import java.io.PrintWriter;
        import java.util.Date;

        /**
        * @author fzy
        * @date 2023/11/28 19:26
        */
        public class AServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        // 向request域中绑定数据
        Date time = new Date();
        request.setAttribute("sysTime", time);
        // 使用Servlet当中的转发机制
        // 执行了AServlet之后,跳转到BServlet(这个资源跳转可以使用转发机制来完成)

        // 思考:这样做可以吗?
        // 在AServlet当中new一个BServlet对象,然后调用BServlet对象的doGet方法,把request对象传过去。
        /* BServlet bServlet = new BServlet();
        bServlet.doGet(request, response); */
        // 这个代码虽然可以实现功能,但是Servlet对象不能自己由程序员来new。
        // 自己new的Servlet对象生命周期不受Tomcat服务器的管理。

        // 怎么转发?代码怎么写?
        // 第一步:获取请求转发器对象
        // 把"/b"这个路径包装到请求转发器当中,相当于把下一个跳转的资源的路径告知给Tomcat服务器了
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/b");
        // 第二步:调用请求转发器RequestDispatcher的forward方法。进行转发。
        // 转发的时候:这两个参数很重要。request和response都是要传递给下一个资源的
        requestDispatcher.forward(request, response);

        //// 一行代码搞定转发。
        //request.getRequestDispatcher("/b").forward(request, response);
        }
        }
      • BServlet

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        package com.f.javaweb.servlet;

        import jakarta.servlet.ServletException;
        import jakarta.servlet.http.HttpServlet;
        import jakarta.servlet.http.HttpServletRequest;
        import jakarta.servlet.http.HttpServletResponse;

        import java.io.IOException;
        import java.io.PrintWriter;
        import java.util.Date;

        /**
        * @author fzy
        * @date 2023/11/28 19:38
        */
        public class BServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        // 从request域中获取数据
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        Date sysTime = (Date) request.getAttribute("sysTime");
        out.print("BSerlvet从request域中获取的系统的当前时间: " + sysTime + "<br/>");
        }
        }
      • 跳转的核心代码

        1
        2
        3
        4
        5
        6
        7
        // 第一步:获取请求转发器对象
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/b");
        // 第二步:调用请求转发器RequestDispatcher的forward方法。进行转发。
        requestDispatcher.forward(request, response);

        // 也可以一行代码搞定转发。
        request.getRequestDispatcher("/b").forward(request, response);
    8. HttpServletRequest 接口的其他常用方法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      // 获取客户端的IP地址
      String remoteAddr = request.getRemoteAddr();
      // request.getRemoteHost(); 获取远程主机地址
      // request.getRemotePort(); 获取端口

      // get请求在请求行上提交数据
      // post请求在请求体中提交数据
      // 设置请求体的字符集(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决GET请求的乱码问题)
      // Tomcat10之后,request请求体当中的字符集默认就是UTF-8,不需要设置字符集,不会出现乱码问题。
      // Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?在获取请求体数据之前执行以下代码。
      request.setCharacterEncoding("UTF-8");
      String name = request.getParameter("name");

      // 在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?
      response.setContentType("text/html;charset=UTF-8");
      // 在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了。

      // 注意一个细节
      // 在Tomcat10包括10在内之后的版本,中文将不再出现乱码(这也体现了中文地位的提升)

      // get请求乱码问题怎么解决?
      // get请求发送的时候,数据是在请求行上提交的,不是在请求体当中提交的。
      // get请求乱码怎么解决
      // 方案:修改CATALINA_HOME/conf/server.xml配置文件
      <Connector port="8080" protocol="HTTP/1.1"
      connectionTimeout="20000"
      redirectPort="8443"
      maxParameterCount="1000"
      URIEncoding="UTF-8" //加入的部分
      />
      // 注意:从Tomcat8之后,URIEncoding的默认值就是UTF-8,所以GET请求也没有乱码问题了

      // 获取应用的根路径
      String contextPath = request.getContextPath(); // /aaa

      // 获取请求方式
      String method = request.getMethod();

      // 获取请求的URI
      String uri = request.getRequestURI(); // /aaa/testRequest

      // 获取servlet path,不带项目名
      String servletPath = request.getServletPath(); // /testRequest

思考

  • 两个 Servlet 对象怎么共享数据?

    • 将数据放到 ServletContext 应用域当中,当然是可以的,但是应用域范围太大,占用资源太多,不建议使用。
    • 可以将数据放到 request 域当中,然后 AServlet 转发到 BServlet,保证 AServletBServlet 在同一次请求当中,这样就可以做到两个 Servlet,或者多个 Servlet 共享同一份数据。
  • 转发的下一个资源必须是一个 Servlet 吗?

    • 不一定,只要是 Tomcat 服务器当中的合法资源,都是可以转发的。例如:html…

      1
      2
      // 可以转发到一个Servlet,也可以转发到一个HTML,只要是WEB容器当中的合法资源即可。
      request.getRequestDispatcher("/test.html").forward(request, response);
    • 注意:转发的时候,路径的写法要注意,转发的路径以 “/” 开始,不加项目名

注意

  • 关于 request 对象中两个非常容易混淆的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // uri?username=zhangsan&userpwd=123&sex=1
    String username = request.getParameter("username");

    // 之前一定是执行过:request.setAttribute("name", new Object())
    Object obj = request.getAttribute("name");

    // 以上两个方法的区别是什么?
    // **第一个方法:获取的是用户在浏览器上提交的数据。**
    // **第二个方法:获取的是请求域当中绑定的数据。**

2.6 ★★★转发和重定向

  • 在一个 web 应用中可以通过两种方式,来完成资源的跳转

    • 第一种方式:转发
    • 第二种方式:重定向
  • 转发和重定向有什么区别?

    • 代码上有什么区别?

      • 转发:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        // 获取请求转发器对象
        // 告诉Tomcat下一次请求要发给哪个Servlet对象
        RequestDispatcher dispatcher = request.getRequestDispatcher("/dept/list");
        // 调用请求转发器对象的forward方法完成转发
        // 传入request, response 保证为同一次请求,保证请求域相同
        dispatcher.forward(request, response);

        // 合并一行代码
        request.getRequestDispatcher("/dept/list").forward(request, response);
        // 转发的时候是一次请求,不管你转发了多少次。都是一次请求。
        // AServlet转发到BServlet,再转发到CServlet,再转发到DServlet,不管转发了多少次,都在同一个request当中。
        // 这是因为调用forward方法的时候,会将当前的request和response对象传递给下一个Servlet。
      • 重定向:

        1
        2
        3
        4
        5
        // 注意:路径上要加一个项目名。为什么?
        // 浏览器发送请求,请求路径上是需要添加项目名的。
        // 以下这一行代码会将请求路径“/oa/dept/list”发送给浏览器
        // 浏览器会自发的向服务器发送一次全新的请求:/oa/dept/list
        response.sendRedirect("/oa/dept/list");

      注意

      1. 转发是通过 request 对象来得到 RequestDispatcher 对象,再进行转发;重定向是通过 response 对象来进行重定向。
      2. 转发的路径不需要带项目名,重定向的路径要加上项目名。因为转发是服务器来控制的(可以看作是后端),重定向是浏览器来执行的(可以看作是前端)
    • 形式上有什么区别?

      • 转发(一次请求):

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

        ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/JavaWeb-notebook/HTTP/转发.png)

      • 重定向(一次重定向总共会有两次请求、两次重定向总共会有三次请求…):

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

        ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/JavaWeb-notebook/HTTP/重定向.png)

    • 转发和重定向的本质区别:

      • 转发:是由 WEB 服务器来控制的,A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的,这些跳转的资源之间共享同一个 requestresponse

      • 重定向:是浏览器完成的。具体跳转到哪个资源,是浏览器说了算。

  • 使用一个例子去描述这个转发和重定向:

    • 借钱(转发:发送了一次请求)
      • 杜老师没钱了,找张三借钱,其实张三没有钱,但是张三够义气,张三自己找李四借了钱,然后张三把这个钱给了杜老师,杜老师不知道这个钱是李四的,杜老师只求了一个人。杜老师以为这个钱就是张三的。
    • 借钱(重定向:发送了两次请求)
      • 杜老师没钱了,找张三借钱,张三没有钱,张三有一个好哥们,叫李四,李四是个富二代,于是张三将李四的家庭住址告诉了杜老师,杜老师按照这个地址去找到李四,然后从李四那里借了钱。显然杜老师在这个过程中,求了两个人。并且杜老师知道最终这个钱是李四借给他的。
  • 转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?

    • 如果在上一个 Servlet 当中向 request 域当中绑定了数据,希望从下一个 Servlet 当中把 request 域里面的数据取出来,则使用转发机制。
    • 剩下所有的请求均使用重定向(重定向使用较多)。

思考

  • 跳转的下一个资源有没有要求呢?必须是一个 Servlet 吗?

    • 不一定,跳转的资源只要是服务器内部合法的资源即可,包括:Servlet、JSP、HTML…
  • 转发会存在浏览器的刷新问题,由于请求转发的前端路径不会发生变化,所以前端进行刷新时会重新执行一次与之前相同的请求,如果之前执行的请求为向数据库插入信息,则使用转发每次进行刷新都会重新执行一次插入操作。

    使用重定向则不会,重定向是发送一次与之前请求无关的一次新请求。

---------------The End---------------