JavaWeb学习Day6——Request&Response

122 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

request:获取 请求数据

  • 浏览器会发送HTTP请求到后台服务器[Tomcat]
  • HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
  • 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
  • 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
  • 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务

response:设置 响应数据

  • 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
  • 把响应数据封装到response对象中
  • 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
  • 浏览器最终解析结果,把内容展示在浏览器给用户浏览

Request对象

Request继承体系

image.png

  • 当我们的Servlet类实现的是Servlet接口的时候,service方法中的参数是ServletRequest和ServletResponse
  • 当我们的Servlet类继承的是HttpServlet类的时候,doGet和doPost方法中的参数就变成HttpServletRequest和HttpServletReponse

我们就需要用到Request继承体系中的RequestFacade:

  • 该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
  • Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat]来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
  • 要想了解RequestFacade中都提供了哪些方法,我们可以直接查看JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档,因为RequestFacade实现了其接口就需要重写接口中的方法

Request获取请求数据

HTTP请求数据总共分为三部分内容,分别是==请求行、请求头、请求体==

获取请求行数据

请求行包含三块内容,分别是请求方式请求资源路径HTTP协议及版本

image.png 对于这三部分内容,request对象都提供了对应的API方法来获取,具体如下:

获取请求方式: GET String getMethod()

获取虚拟目录(项目访问路径): /request-demo String getContextPath()

获取URL(统一资源定位符): http://localhost:8080/request-demo/req1 StringBuffer getRequestURL()

获取URI(统一资源标识符): /request-demo/req1 String getRequestURI()

获取请求参数(GET方式): username=zhangsan&password=123 String getQueryString()

获取请求头数据

对于请求头的数据,格式为key: value如下:

image.png 所以根据请求头名称获取对应值的方法为: String getHeader(String name)

获取请求体数据

浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变更为POST,请求体中的数据格式如下:

image.png 对于请求体中的数据,Request对象提供了如下两种方式来获取其中的数据,分别是:

  • 获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法 ServletInputStream getInputStream() 该方法可以获取字节
  • 获取字符输入流,如果前端发送的是纯文本数据,则使用该方法 BufferedReader getReader()

IDEA快速创建Servlet

使用通用方式获取请求参数后,屏蔽了GET和POST的请求方式代码的不同,则代码可以定义如下格式:

image.png 由于格式固定,所以我们可以使用IDEA提供的模板来制作一个Servlet的模板,这样我们后期在创建Servlet的时候就会更高效,具体如何实现: (1)按照自己的需求,修改Servlet创建的模板内容

image.png (2)使用servlet模板创建Servlet类

image.png

再次创建Servlet:

image.png

中文乱码问题

中文乱码解决方案

  • POST请求和GET请求的参数中如果有中文,后台接收数据就会出现中文乱码问题

    GET请求在Tomcat8.0以后的版本就不会出现了

  • POST请求解决方案是:设置输入流的编码

    request.setCharacterEncoding("UTF-8");
    注意:设置的字符集要和页面保持一致
    
  • 通用方式(GET/POST):需要先解码,再编码

    new String(username.getBytes("ISO-8859-1"),"UTF-8");
    
  1. URL编码实现方式:
  • 编码:

    URLEncoder.encode(str,"UTF-8");
    
  • 解码:

    URLDecoder.decode(s,"ISO-8859-1");
    

Request请求转发

请求转发(forward):一种在服务器内部的资源跳转方式。

image.png 请求转发的实现方式:

req.getRequestDispatcher("资源B路径").forward(req,resp);

针对上述需求,具体的实现步骤为:

1.创建一个RequestDemo5类,接收/req2的请求,在doGet方法中打印demo2

2.创建一个RequestDemo6类,接收/req3的请求,在doGet方法中打印demo3

3.在RequestDemo2的方法中使用

req.getRequestDispatcher("/req3").forward(req,resp)进行请求转发

4.启动测试

@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo2");
        request.getRequestDispatcher("/req3").forward(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
@WebServlet("/req3")
public class RequestDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo3");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

image.png 请求转发资源间共享数据:使用Request对象

此处主要解决的问题是把请求从/req2转发到/req3的时候,如何传递数据给/req3。 需要使用request对象提供的三个方法:

  • 存储数据到request域[范围,数据是存储在request对象]中
request.setAttribute("msg","hello");
  • 根据key获取值
request.getAttribute("msg");
  • 根据key删除该键值对
removeAttribute(String name);

Response对象

Reponse的继承体系和Request的继承体系也非常相似:

image.png

Response设置响应数据功能介绍

1.响应行

image.png 比较常用的就是设置响应状态码: void setStatus(int sc); 2.响应头

image.png 设置响应头键值对:void setHeader(String name,String value); 3.响应体

image.png 对于响应体,是通过字符、字节输出流的方式往浏览器写,

获取字符输出流: PrintWriter getWriter();

获取字节输出流:ServletOutputStream getOutputStream();

Respones请求重定向

Response重定向(redirect):一种资源跳转方式。

image.png (1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求

(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径

(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B

(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫==重定向==

重定向的实现方式:

resp.setStatus(302);
resp.setHeader("location","资源B的访问路径");

image.png 针对上述需求,具体的实现步骤为:

1.创建一个ResponseDemo1类,接收/resp1的请求,在doGet方法中打印resp1....

2.创建一个ResponseDemo2类,接收/resp2的请求,在doGet方法中打印resp2....

3.在ResponseDemo1的方法中使用

response.setStatus(302);

response.setHeader("Location","/request-demo/resp2") 来给前端响应结果数据

4.启动测试

虽然功能已经实现,但是从设置重定向的两行代码来看,会发现除了重定向的地址不一样,其他的内容都是一模一样,所以request对象给我们提供了简化的编写方式为:

response.sendRedirect("/request-demo/resp2");

重定向的特点

  • 浏览器地址栏路径发送变化
  • 可以重定向到任何位置的资源(服务内容、外部均可)
  • 两次请求,不能在多个资源使用request共享数据

==请求重定向==和==请求转发==对比 image.png

路径问题

转发的时候路径上没有加/request-demo而重定向加了,那么到底什么时候需要加,什么时候不需要加呢?

image.png 其实判断的依据很简单,只需要记住下面的规则即可:

  • 浏览器使用:需要加虚拟目录(项目访问路径)
  • 服务端使用:不需要加虚拟目录 对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录

对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。

在重定向的代码中,/request-demo是固定编码的,如果后期通过Tomcat插件配置了项目的访问路径,那么所有需要重定向的地方都需要重新修改,该如何优化?

我们可以在代码中动态去获取项目访问的虚拟目录,具体如何获取,我们可以借助前面咱们所学习的request对象中的getContextPath()方法,修改后的代码如下:

@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("resp1..");

        String contextPath = request.getContextPath();
        response.sendRedirect(contextPath+"/resp2");
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

Response响应字符数据

要想将字符数据写回到浏览器,我们需要两个步骤:

  • 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
  • 通过字符输出流写数据: writer.write("aaa");
@WebServlet("/resp3")
public class ResponseDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write("aaa");
        writer.write("<h1>aaa</h1>");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
  • content-type,告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签

  • 一次请求响应结束后,response对象就会被销毁掉,所以不要手动关闭流。

Response响应字节数据

要想将字节数据写回到浏览器,我们需要两个步骤:

  • 通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();
  • 通过字节输出流写数据: outputStream.write(字节数据);

上述代码中,对于流的copy的代码还是比较复杂的,所以我们可以使用别人提供好的方法来简化代码的开发,具体的步骤是: (1)pom.xml添加依赖

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

(2)调用工具类方法 //fis:输入流 //os:输出流 IOUtils.copy(fis,os);

@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        FileInputStream inputStream = new FileInputStream("E:/Users/lenovo/Desktop");
        ServletOutputStream outputStream = response.getOutputStream();
        IOUtils.copy(inputStream,outputStream);
        inputStream.close();
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}