携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
request:获取 请求数据
- 浏览器会发送HTTP请求到后台服务器[Tomcat]
- HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
- 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
- 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
- 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
response:设置 响应数据
- 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
- 把响应数据封装到response对象中
- 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
- 浏览器最终解析结果,把内容展示在浏览器给用户浏览
Request对象
Request继承体系
- 当我们的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协议及版本
对于这三部分内容,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如下:
所以根据请求头名称获取对应值的方法为: String getHeader(String name)
获取请求体数据
浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变更为POST,请求体中的数据格式如下:
对于请求体中的数据,Request对象提供了如下两种方式来获取其中的数据,分别是:
- 获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法 ServletInputStream getInputStream() 该方法可以获取字节
- 获取字符输入流,如果前端发送的是纯文本数据,则使用该方法 BufferedReader getReader()
IDEA快速创建Servlet
使用通用方式获取请求参数后,屏蔽了GET和POST的请求方式代码的不同,则代码可以定义如下格式:
由于格式固定,所以我们可以使用IDEA提供的模板来制作一个Servlet的模板,这样我们后期在创建Servlet的时候就会更高效,具体如何实现:
(1)按照自己的需求,修改Servlet创建的模板内容
(2)使用servlet模板创建Servlet类
再次创建Servlet:
中文乱码问题
中文乱码解决方案
-
POST请求和GET请求的参数中如果有中文,后台接收数据就会出现中文乱码问题
GET请求在Tomcat8.0以后的版本就不会出现了
-
POST请求解决方案是:设置输入流的编码
request.setCharacterEncoding("UTF-8"); 注意:设置的字符集要和页面保持一致 -
通用方式(GET/POST):需要先解码,再编码
new String(username.getBytes("ISO-8859-1"),"UTF-8");
- URL编码实现方式:
-
编码:
URLEncoder.encode(str,"UTF-8"); -
解码:
URLDecoder.decode(s,"ISO-8859-1");
Request请求转发
请求转发(forward):一种在服务器内部的资源跳转方式。
请求转发的实现方式:
req.getRequestDispatcher("资源B路径").forward(req,resp);
针对上述需求,具体的实现步骤为:
1.创建一个RequestDemo5类,接收/req2的请求,在doGet方法中打印
demo22.创建一个RequestDemo6类,接收/req3的请求,在doGet方法中打印
demo33.在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);
}
}
请求转发资源间共享数据:使用Request对象
此处主要解决的问题是把请求从/req2转发到/req3的时候,如何传递数据给/req3。
需要使用request对象提供的三个方法:
- 存储数据到request域[范围,数据是存储在request对象]中
request.setAttribute("msg","hello");
- 根据key获取值
request.getAttribute("msg");
- 根据key删除该键值对
removeAttribute(String name);
Response对象
Reponse的继承体系和Request的继承体系也非常相似:
Response设置响应数据功能介绍
1.响应行
比较常用的就是设置响应状态码:
void setStatus(int sc);
2.响应头
设置响应头键值对:void setHeader(String name,String value);
3.响应体
对于响应体,是通过字符、字节输出流的方式往浏览器写,
获取字符输出流: PrintWriter getWriter();
获取字节输出流:ServletOutputStream getOutputStream();
Respones请求重定向
Response重定向(redirect):一种资源跳转方式。
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫==重定向==
重定向的实现方式:
resp.setStatus(302);
resp.setHeader("location","资源B的访问路径");
针对上述需求,具体的实现步骤为:
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共享数据
==请求重定向==和==请求转发==对比
路径问题
转发的时候路径上没有加/request-demo而重定向加了,那么到底什么时候需要加,什么时候不需要加呢?
其实判断的依据很简单,只需要记住下面的规则即可:
- 浏览器使用:需要加虚拟目录(项目访问路径)
- 服务端使用:不需要加虚拟目录 对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录
对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。
在重定向的代码中,/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);
}
}