对servlet中的两个对象(Request与Response)的理解

对servlet中的两个对象(Request与Response)的理解

Request

​ 在上一篇中我们研究了关于Servlet的作用与相关的配置问题(没有接触的小伙伴可以去接触一下),我们知道在servlet接口中最重要的或者说我们应用最多的就是Servlet方法。

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("努力学习的小黄");
    }

​ 我们可以发现在重写方法service中有ServletRequest类型的参数,还有ServletResponse类型的参数,它们本质上对应的其实就是前端与后端互动时候的Request与Request的两个过程(即请求与响应)。接下来,我们就主要来看这两个过程,首先是Request。javaee中为针对Servlet为Request提供了一套接口ServletRequest(上面的servise中可以看到),后来javaee又针对Servlet与Web进行互动替Servlet完成了接口的封装并且在ServletRequest的基础上又为它实现了一个子接口HttpServletRequest。

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");
    }

​ 在安装了TomcatWeb服务器之后,内部的Tomcat又在javaee的基础上定义了HttpServletRequest的实现类名字叫做RequestFacade,Tomcat会自动完成对这个的解析,封装为Request对象,我们直接调用即可。

比如下面来说:

http://localhost:8080/MavenWeb_Tomcat_war/ZhangBing?username=%E9%BB%84%E5%85%83%E5%8D%93&password=197953&submit=%E6%8F%90%E4%BA%A4

### request请求内容详情:

​ getMethon():返回请求的请求方式(get or post)在这个里面是GET,getContextPath():获取虚拟目录/项目访问路径 在这个里面就会返回 /MavenWeb_Tomcat_war (这里没有设置所以项目名就会作为这个虚拟目录名) ,获取统一资源定位符getRequestURL():在这个例子中是http://localhost:8080/MavenWeb_Tomcat_war/ZhangBing ,获取统一资源标识符getRequestURI:/MavenWeb_Tomcat_war/ZhangBing

最后就是获取请求参数: getQueryString():获取请求参数,不过只针对用GET方式来获取。

@WebServlet("/ZhangBing")
public class ServletMaker3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        System.out.println("请求方式:"+method);  //GET
        String contextPath = req.getContextPath();
        System.out.println("虚拟目录:"+contextPath);       ///MavenWeb_Tomcat_war
        StringBuffer requestURL = req.getRequestURL();
        System.out.println("统一资源定位符:"+requestURL);    //http://localhost:8080/MavenWeb_Tomcat_war/ZhangBing
        String requestURI = req.getRequestURI();
        System.out.println("统一资源标识符:"+requestURI);     ///MavenWeb_Tomcat_war/ZhangBing
        String queryString = req.getQueryString();
        System.out.println("获取请求参数:"+queryString);     //username=%E5%BC%A0%E9%9B%AA%E5%B3%B0&password=13371237198733&submit=%E6%8F%90%E4%BA%A4
        // http://192.168.152.88:8080/MavenWeb_Tomcat_war/ZhangBing?username=%E9%BB%84%E5%85%83%E5%8D%93&password=197953&submit=%E6%8F%90%E4%BA%A4
    }

​ 请求头:对于谷歌浏览器来说,都是用默认的名称name = “User-Agent”,作为请求头,运用到的方法就是getHeader(),示范:

String username = req.getHeader("User-Agent");
        System.out.println(username);
//输出结果:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.0.9071 SLBChan/1050

​ 对于请求体来说(只有post)的,对于Post的请求方式,请求体会自动的把请求参数封装在请求体中,这个过程在输出时是借助于字符输入流BufferedReader中的getReade()方法来执行的。但是需要注意在Request执行时需要注意一点,针对不同的内容会输出不同的表达形式,针对要输入的字符串,我们要借助于字符输入流BufferedReader,但是比如我们要是输入图片或者视频音频之类的,这个时候使用字符输入流BufferedReader就会表现出其自身的局限性,针对这类问题,我们就需要借助字节输入流来帮助。

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      //Post的执行逻辑
      String method = req.getMethod();    //请求方式
      String contextPath = req.getContextPath();     //虚拟目录
      StringBuffer requestURL = req.getRequestURL();  //统一资源定位符
      String requestURI = req.getRequestURI();       //统一资源标识符
      String queryString = req.getQueryString();      //获取请求参数
      System.out.println("请求方式:"+method);
      System.out.println("虚拟目录:"+contextPath);
      System.out.println("统一资源定位符:"+requestURL);
      System.out.println("统一资源标识符:"+requestURI);
      System.out.println("获取请求参数"+queryString);         //Post请求中会不存在

      BufferedReader reader = req.getReader();        //request调用getReader()方法,来获得把请求参数封装在请求体中的字符输入流
      String s = reader.readLine();           //读取字符输入流的一行
      System.out.println(s);                  //输出请求体
  }

在前台与后台互动的过程中我们比较关心的就是对于request其中的请求数据,我们先复习一下之前学过的,对于之前来说,获取请求参数的方式因其请求方式的不同而不同,对于GET请求方式来说主要的是getQueryString方法返回请求参数的字符串,对于POST请求参数来说,我们会借助于getReader方法来创建有关请求体的字符输入流,通过读取字符输入流的第一行,来输出请求体内容,但是在我们实际要开发的时候,类似这样的过程太过于繁琐,我们希望于通过一种简单的方法来统一输出GET的请求参数与POST请求参数,如何做,其实在Servlet中已经找到了解决办法。

​ 具体的方法就是应用getParameterMap,getParameter,getParameterValues这三种方法,具体应用是这样的,getParameterMap方法用来返回请求参数的键值对类型,如果一个键对应多个不同的值,改方法会自动的将多个不同的值进行封装,封装成数组的形式,具体范例如下:

    System.out.println("参数的具体内容:");
         Map<String, String[]> parameterMap = req.getParameterMap();
         Set<String> strings = parameterMap.keySet();
         for (String key : strings)   //第一次遍历
         {
             System.out.print(key+":");
             String[] strings1 = parameterMap.get(key);
             for (String i:strings1){         //第二次遍历
                 System.out.print(i+" ");
             }
             System.out.println( );
         }
 ```

​ 用getParameter的方法是针对已知的键名来返回相应的键对应的值(注意这个值必须是单个的值不可以是数组)

       String username = request.getParameter("username");
       System.out.println("username:"+username);
       String password = request.getParameter("password");
       System.out.println("password:"+password);
       String parameter = request.getParameter("Do-submit");
       System.out.println("Do-submit:"+parameter);

​ 而专门用来返回键对应的值是数组的也有专门的方法,那就是getParameterValues方法

        String[] checkboxes = request.getParameterValues("checkbox");
        System.out.print("checkbox: ");
        for (String i : checkboxes)
        {
            System.out.print(i+" ");
        }
        System.out.println( );
    }

​ 到此为止三种方法就全部讲完了,应当注意使用这样的方法时,Tomcat对浏览器的其数据编码解析可能与idea编码不一致,一种比较好的办法就是提前设置一下,让他们的编码统一即可,使用的方法是req.setCharacterEncoding(“UTF-8”)

请求转发

​ 关于这个请求转发,请求的来源地我们可以看成浏览器,请求发出以后会来到服务器中去,服务器A区可以将该请求数据转发到服务器B区,最终是由服务器B区对浏览器做出响应。那么这个过程就是一个请求转发的过程。我们习惯称之为forword,它的代码比较简单:

        request.getRequestDispatcher("/ServletDemo5").forward(request,response);    //将请求转发到ServletDemo5中

​ 在从服务区A转到服务区B的这个过程中是通过以request域来存储共享数据的,这一项动作包括1.存储数据到request域中,2.根据key值,获取对应的键值。3.用完了之后需将共享数据域的数据进行清空删除处理。它们分别对应着下面的几种方法

  String password = request.getParameter("password");
        String[] checkboxes = request.getParameterValues("checkbox");
        request.setAttribute("password", password);      //将键是password的键值对存储数据到request域中
        request.setAttribute("checkbox",checkboxes);     //将键是checkbox的键值对存储数据到request域中
        request.getRequestDispatcher("/ServletDemo7").forward(request,response);     //将请求转发到ServletDemo7中
 String username = (String) request.getAttribute("password");    //接收键为password的值
        String[] checkbox = (String[]) request.getAttribute("checkbox");  //接收键为checkbox的值     
  request.removeAttribute(String name);     //根据key 删除request域中的键值对

请求的特点:首先对于请求来说,我们浏览器的地址栏处并不会发生改变(比如说我们访问的是服务器A区的资源,A通过请求转发将请求转发到B,在这个过程中浏览器的地址栏上会一直显示着A区资源的路径,而不会跳转到服务器B区资源的地址,这就是请求转发的特点之一),请求转发只能转发到服务器的内部资源,且每次只进行一次,而且在转发的过程中可以在转发的资源间使用request共享域。

Response

​ 我们学习可以用类比的思想,上面已经了解到request是专门用来请求数据的,重点是它会将用户已经输入好的请求有关的重要参数发送到服务器,并且已经了解到了相对应的一系列"get"方法,那么response作为响应,就是用来向浏览器重新设置数据并进行发送的一系列手段,并且应该会出现对应的"set"方法来由后台设置数据已完成响应操作。

​ 同样的java也Servlet提供了相应的Response接口,java提供的请求对象根接口ServletResponse,同时为了接口可以更好的封装Http协议,java在ServletResponse的基础上添加了封装了Http协议的HttpServletResponse接口,它是ServletResponse的一个子接口,而当我们安装了Tomcat服务器后,Tomcat服务器会自动帮我们完成对子接口的实现操作,实现类名叫做ResponseFacade(是由Tomcat定义的)

​ Response是用来设置响应数据的,数据的响应同样的也分成响应头,响应行,响应体。

响应行中,比较重要的是响应状态码

void setStatus(int sc);     //设置响应状态码

响应头与请求头类似都是应用键值对

void setHeader(String name ,String value);          //设置响应头的键值对 ,在请求头中是name = "User-Agent";

响应体

​ 响应体实际上是通过流的方式往浏览器是输出(字符输出流,字节输出流) ,当时获得请求体的时候Post我们应用的是字节字符输入流,而类似的我们在设置响应体的时候可以设置一下字节字符输jut出流去设置。getWrite字符输出流,getOutputStream字节输出流,对于文字形式来说,我们通常是应用字符的数据,对于输出图片格式或者音频视频等等,我们通常是应用字节输出流。

Response的重定向,我们类比请求转发,重定向也是一种资源跳转的方式,下面具体演示一下:

   1. 浏览器向服务器区A发送请求,但是服务器A区发现自己解决不了这个问题。
   1. 服务器A区向浏览器发送一个响应,重点就是说你这个请求我解决不了,你去找服务器B区吧,那个服务器B区是几号楼几号公寓。
   1. 浏览器受到响应之后,就会对服务器B区重新发送请求。

​ 服务器A区对浏览器发送响应,发送状态码:302(表示着浏览器提出的请求我是完成不了的,浏览器还是去找其他的服务器资源吧)

​ 同时服务器A区还应该提供给浏览器一个资源B的地址(意思就是让她去找B,B可以解决这个请求)

有关代码:

response.setStatus(302);       //状态码
response.setHeader("location","资源B的路径");    //B资源的地址,其实就是设置响应头

简化形式:

response.sendRedirect("资源B的路径");

​ 重定向与请求转发的区别:在经过资源跳转后,请求转发对应的浏览器地址栏不发生变化,而重定向后的浏览器地址栏会发生变化,其实本质原因是因为重定向的时候,浏览器在整个过程中发送了两次请求,而请求转发只发送一次请求。重定向可以重定向到任意资源,但是相比之下,请求转发的转发只能被限制到当前服务器的内部资源,另外重定向并不限制于在服务器内部进行转发,因此在重定向其中的过程中数据并不会储存在request共享域中,而请求转发的过程,其中的数据就会在request共享域中。

​ 一般情况下,给浏览器使用的路径需要虚拟目录,而如果是关于应用服务器的则不需要加上虚拟目录,目前来看,请求转发是不需要加上虚拟目录的(req.getRequestDispatcher(“路径”))。

​ 这里有一个小细节,即对于重定向来说前面我们已经看到了其中的方法,其中需要传递一个参数(带虚拟目录的路径),对于路径的获取,这里我们通常使用request对象中的getContextPath方法与我们的最终路径进行合并处理。具体范式如下:

        String contextPath = request.getContextPath();
        response.sendRedirect(contextPath+"/ServletTest5");

Response响应字符数据

​ 具体操作如下:

​ 通过Response对象获取到字符输出流:

PrintWriter writer = response.getWriter();

​ 写数据:

writer.write("abc");

​ 这里有一个小细节,对于专业程序员来说我们通常利用响应体发出的是一个html格式的标签,为了让text格式与html格式共存,我们可以提前设置一下响应头

writer.setHeader("content-type","text/html;charset=UTF-8");    //设置响应头,使其能够识别html格式,后面的charset=UTF-8是为了防止出现输出字符流乱码问题。

Response响应字节数据

​ 类比于上式:

​ 通过Response对象获取字节输出流

ServletOutputStream outputStream = resp.getOutputStream();

​ 写数据

outputStream.write(字节数据);

​ 我们知道字节输出流需要有相应的字节数据输入,我们通常的方式是再次写一个字节输入流,传入相关的字节数据后,将字节输入流中的数据复制给字节输出流使其输出。现在的话,有一种更为便利的方式就是首先利用IOUtils工具类,首先导入坐标

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>

​ 然后使用IOUtils中的copy方法即可完成从字节输入流到字节输出流的复制过程,并且完成输出操作

IOUtils.copy(输入流,输出流);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值