7 HTTP
定义:HyperText Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则
浏览器和服务器之间数据传输的情况:
浏览器向服务器发送请求,服务器向浏览器提交响应。
作为HTTP而言,就是要让双方看得懂对方在说什么。
HTTP 协议特点:
1.基于TCP协议:面向连接,安全
【我们在复习java时复习过了协议,TCP协议是传输控制协议,传输需要建立连接,三次握手】
2.基于请求-响应模型的:一次请求对应一次响应
3.HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的。
缺点:多次请求间不能共享数据。 优点:速度快
记忆:HTTP 协议特点是:安全,1v1,记不住,因为1V1所以无法共享,但是自己干自己的,所以速度快。
【如何解决不能共享数据?使用会话技术(Cookie、Session)来解决】
7.1 HTTP-请求数据格式
请求数据分为3部分:
1.请求行:请求数据的第一行。
其中GET表示请求方式,/表示请求资源路径,HTTP/1.1表示协议版本
2.请求头:第二行开始,格式为key:value形式。
3.请求体: POST请求的最后一部分,存放请求参数
案例:
GET / HTTP/1.1
Host: www.itcast.cn
Connection: keep-alive
User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
…
-------------------------
POST / HTTP/1.1
Host: www.itcast.cn
Connection: keep-alive
Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
username=superbaby&password=123456
7.1.1 常见的HTTP请求头
Host: 表示请求的主机名
【记忆:LocalHost经常见到,是主机,所以Host表示请求的主机名】
User-Agent: 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 ... Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...) like Gecko;
【记忆:Agent是代理人,用户代理人,就是用户通过浏览器去看页面,那么含义就是这个代理是什么浏览器,它的版本是什么】
Accept:表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有;
【记忆:Agent是代理人,用户代理人,就是用户通过浏览器去看页面,那么含义就是这个代理是什么浏览器,它的版本是什么】
Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
【记忆:平常使用浏览器的时候,如果是外文网页会有自动翻译,所以浏览器是会需要知道网页的语言的】
Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等。
【记忆:因为TCP协议会进行大数据量的传输,所以大数据的编码要压缩】
7.2 HTTP-响应数据格式
响应数据分为3部分:
1.响应行:响应数据的第一行。
其中HTTP/1.1表示协议版本,200表示响应状态码,OK表示状态码描述
2.响应头:第二行开始,格式为key:value形式
3.响应体: 最后一部分。存放响应数据
案例:
HTTP/1.1 200 OK
Server: Tengine
Content-Type: text/html
Transfer-Encoding: chunked…
<html>
<head>
<title></title>
</head>
<body></body>
</html>
状态码可以在此连接查询:状态 | Status - HTTP 中文开发手册 - 开发者手册 - 腾讯云开发者社区-腾讯云
7.2.1 常见的HTTP 响应头:
Content-Type:表示该响应内容的类型,例如text/html,image/jpeg;
Content-Length:表示该响应内容的长度(字节数);
Content-Encoding:表示该响应压缩算法,例如gzip;
Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒
7.3 对照记忆
过程 | 请求数据 | 响应数据 | |
GET请求 | POST请求 | ||
格式 | 1.请求行 | 1.响应行 | |
GET / HTTP/1.1 | POST / HTTP/1.1 | HTTP/1.1 200 OK | |
2.请求头 | 2.响应头 | ||
key:value | |||
\ | 3.请求体 | 3.响应体 | |
\ | 请求参数 | 响应数据 |
8 Web 服务器
定义:Web服务器是一个应用程序(软件)。
功能:
- 封装HTTP协议操作,简化开发
- 可以将web项目部署到服务器中,对外提供网上浏览服务
优点:对 HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。
8.1 Tomcat服务器
定义:Tomcat是Apache 软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量JavaEE规范。
Tomcat 也被称为 Web容器、Servlet容器。Servlet 需要依赖于 Tomcat才能运行
JavaEE:Java Enterprise Edition,Java企业版。指Java企业级开发的技术规范总和。包含13项技术规范:JDBC、JNDI、EJB、RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF
官网:https://tomcat.apache.org/
8.2 Tomcat使用
这部分没什么知识性的东西。
需要了解:下载、安装、卸载、启动、关闭、配置、部署项目。
IDEA中使用 Tomcat – Tomcat Maven 插件:
<build>
<plugins>
<!--Tomcat 插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8081</port><!--访问端口号-->
<path>/</path><!--项目访问路径-->
</configuration>
</plugin>
</plugins>
</build>
9 Servlet
定义:Servlet 是 Java提供的一门动态web资源开发技术。
Servlet 是JavaEE 规范之一,其实就是一个接口,我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet
9.1 Servlet的使用与执行流程初探
9.1.1 使用Servlet
1.创建 web项目,导入 Servlet依赖坐标
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>这里一定要设置成provided
</dependency>
</dependencies>
2.定义一个类,实现 Servlet接口,并重写接口中所有方法,并在 service方法中输入一句话
public class ServletDemo1 implements Servlet {
public void service(){
System.out.println("servlet被执行");
}
}
3.配置:在类上使用@WebServlet 注解,配置该 Servlet的访问路径
@WebServlet("/demo1")
public class ServletDemo1 implements Servlet {}
4.访问:启动 Tomcat,浏览器输入URL 访问该Servlet
http://localhost:8081/web-demo/demo1
9.1.2 Servlet执行流程
输入了URL之后,浏览器向服务器发送请求
其中“http://localhost:8081”可以寻找到Tomcat这个web服务器,“web-demo”可以寻找到WEB项目,“demo1”寻找到对应的Servlet实现类
在此过程中,web服务器创建Servlet对象,调用Servlet方法。
之后,服务器向浏览器响应结果。
9.2 Servlet生命周期
生命周期:对象的生命周期指一个对象从被创建到被销毁的整个过程
Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:
1.加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象
@WebServlet(urlPatterns = "/demo",loadOnStartup = 1)
loadOnStartup的值设置为:负整数:第一次被访问时创建Servlet对象
0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
【记忆:不管一个什么东西,拿过来用之前先加载一下非常合情合理,并且web服务器创建servlet对象之前也提到过了。默认情况下访问之后才会创建,而如果要启动创建才需要设置参数值,因此它是不同的特殊情况,而越小的数字在前面也很合理】
2.初始化:在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次
【记忆:Servlet中有五个方法,init()就是用来初始化的,而初始化一次就行了】
3.请求处理:每次请求Servlet时,Servlet容器都会调用Servlet的service()方法对请求进行处理。
【记忆:service方法之前已经在初探中展示过了,毕竟作为servlet总得干活】
4.服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
【记忆:干完活就得终止了,释放资源嘛】
9.3 Servlet方法
1.初始化方法,在Servlet被创建时执行,只执行一次
void init(ServletConfig config)
2.提供服务方法, 每次Servlet被访问,都会调用该方法
void service(ServletRequest req, ServletResponse res)
3.销毁方法,当Servlet被销毁时,调用该方法。在内存释放或服务器关闭时销毁Servlet。只执行一次
void destroy()
4.获取ServletConfig对象
ServletConfig getServletConfig()
5.获取Servlet信息。一般没什么,我们让它return null;即可
String getServletInfo()
在此我们重点讲一下如何获取某个方法的入参。
@WebServlet(urlPatterns = "/demo3",loadOnStartup = 1)
public class ServletDemo3 implements Servlet {
private ServletConfig servletConfig;
初始化方法
public void init(ServletConfig servletConfig) throws ServletException {
this.servletConfig = servletConfig;
System.out.println("init被调用");
}
获取ServletConfig对象方法
public ServletConfig getServletConfig() {
return servletConfig;
}
}
想获取init的servletConfig,可以设定一个成员变量,将值赋给成员变量,这样的话getservletConfig方法可以返回这个变量的值给调用它的对象。
9.4 Servlet 体系结构
Servlet接口作为Servlet体系根接口
GenericServlet是Servlet抽象实现类
HttpServlet是对HTTP协议封装的Servlet实现类
在9.1.1中,我们自定义的Servlet类实现了跟体系接口,但开发B/S架构的web项目,都是针对HTTP协议的,所以之后自定义Servlet,要继承HttpServlet类。
9.4.1 HttpServlet 原理
在HTTP协议中,GET 和 POST 请求方式的数据格式不一样,所以在Servlet中处理请求参数,得在service方法中判断请求方式,并且根据请求方式的不同,分别进行处理。
9.4.2 HttpServlet 抽象方法展示
@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get===");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post===");
}
}
其中两个方法,分别针对get请求和post请求进行处理
9.5 Servlet urlPattern配置
之前我们已经展示过例如“@WebServlet("/demo4")”这样的访问路径设置了,我们知道:
Servlet 要想被访问,必须配置其访问路径(urlPattern)
1.一个Servlet,可以配置多个 urlPattern,以数值的形式赋值
例如:
@WebServlet(urlPatterns = {"/demo7","/demo8"})
2.urlPattern有四种配置规则
- 精确匹配
- 目录匹配
- 扩展名匹配
- 任意匹配(不建议使用)
四种匹配规则的优先级:精确路径 > 目录路径 > 扩展名路径 > /* > /
9.5.1 urlPattern 配置规则 精确匹配
配置路径:@WebServlet(“/user/select”)
访问路径:localhost:8081/web-demo/user/select
也就是说,访问的路径和配置的路径需要一模一样
9.5.2 urlPattern 配置规则 目录匹配
配置路径:@WebServlet(“/user/*”)
访问路径:
- localhost:8081/web-demo/user/abc
- localhost:8081/web-demo/user/def
写到user是要一样的,但*代表的就是随便写什么都能匹配到这个servlet
9.5.3 urlPattern 配置规则 扩展名匹配
配置路径:@WebServlet(“*.do”)
访问路径:
- localhost:8081/web-demo/abc.do
- localhost:8081/web-demo/def.do
需要有和配置路径一样的扩展名
9.5.4 urlPattern 配置规则 任意匹配
配置路径:
- @WebServlet(“/”)
- @WebServlet(“/*”)
访问路径:
- localhost:8081/web-demo/abcdeg
- localhost:8081/web-demo/sfjewjghsjd
随便写
/ 和 /* 区别:
- 当我们的项目中的Servlet配置了“/”,会覆盖掉tomcat中的DefaultServlet,当其他的 url-pattern都匹配不上时都会走这个Servlet
- 当我们的项目中配置了“/*”,意味着匹配任意访问路径
9.6 XML 配置方式编写 Servlet
Servlet 从3.0版本后开始支持使用注解配置,3.0版本前只支持 XML 配置文件的配置方式
这种方式比较麻烦,建议使用注解编写
使用步骤:
1.编写 Servlet类
2.在 src/main/webapp/WEB-INF/web.xml中配置该Servlet
案例:
<!-- Servlet全类名-->
<servlet>
<servlet-name>demo13</servlet-name>
<servlet-class>com.xuexi.web.servletdemo13</servlet-class>
</servlet>
<!-- Servlet访问路径-->
<servlet-mapping>
<servlet-name>demo13</servlet-name>
<url-pattern>/demo13</url-pattern>
</servlet-mapping>
10 Request与Response
在上一章中我们学习了Servlet其中有两个参数Request,Response。
-
浏览器会发送 HTTP 请求到后台服务器 [Tomcat]
-
HTTP 的请求中会包含很多请求数据 [ 请求行 + 请求头 + 请求体 ]
-
后台服务器 [Tomcat] 会对 HTTP 请求中的数据进行解析并把解析结果存入到一个对象中
-
所存入的对象即为 request 对象,所以我们可以从 request 对象中获取请求的相关参数
-
获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
【记忆:request对象就从请求中拿东西来用的,他请求啥,如果合适的话就给他看】
-
业务处理完后,后台就需要给前端返回业务处理的结果,即响应数据
-
把响应数据封装到 response 对象中
-
后台服务器 [Tomcat] 会解析 response 对象 , 按照 [ 响应行 + 响应头 + 响应体 ] 格式拼接结果
-
浏览器最终解析结果,把内容展示在浏览器给用户浏览
【记忆:来的时候tomcat把数据解析成“体头行”,走的时候也得把他拼接成体头行返回回去】
小结:
- request对象是用来封装请求数据的对象
- response对象是用来封装响应数据的对象
10.1 Request对象继承体系
ServletRequest接口 Java提供的请求对象根接口
HttpServletRequest接口 Java提供的 对Http协议封装的请求对象接口
RequestFacade类 Tomcat定义的实现类
因此:RequestFacade类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
查看RequestFacade的方法可以查询“JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档”
10.2 Request获取请求数据
10.2.1 获取请求行数据
请求行包含:请求方式,请求资源路径以及HTTP协议版本
方法 | 作用 |
String getMethod()
|
获取请求方式
|
eg. GET/POST | |
String getContextPath()
|
获取虚拟目录(
项目访问路径
)
|
eg. /demo | |
StringBuffer getRequestURL()
|
获取
URL(
统一资源定位符
)
|
eg. http://localhost:8080/
demo/req1
| |
String getRequestURI()
|
获取
URI(
统一资源标识符
)
|
eg. /demo/req1 | |
String getQueryString()
|
获取请求参数
(GET
方式
)
|
eg. username=zhangsan&password=123 |
10.2.2 获取请求头数据
请求头的数据格式为key:value
如:User-Agent:Mozilla/5.0 Chrome/91.0.4472.106
方法 | 功能 |
String getHeader(String name)
| 根据请求头名称获取对应值 |
10.2.3 获取请求体数据
只有POST请求具有请求体,请求体中数据的格式如:
方法 | 功能 |
ServletInputStream getInputStream()
|
获取字节输入流
|
BufferedReader getReader()
|
获取字符输入流【当前端发送的是纯文本数据,建议使用此方法】
|
使用案例:
1.编写一个html页面【位置:src/main/webapp文件夹中】,在页面中设置表单,提交表达则会发送POST请求
<body>
<form action="/tomcatdemo/req3" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit">
</form>
其中:action:form表单提交的请求地址 ;method:请求方式,指定为post
路径
1.究竟如何写路径,何时要加上虚拟目录(项目访问路径),何时不加?
-
浏览器使用 : 需要加虚拟目录 ( 项目访问路径 )
-
服务端使用 : 不需要加虚拟目录
示例:
- 对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录
- 对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。
2.Maven中配置了项目的访问地址,如何解耦?
使用request对象中的getContextPath()方法,如下
简化方式完成重定向
动态获取虚拟目录
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/resp2");
2.在Servlet的doPost方法中获取数据
【创建继承HttpServlet类的Servlet类的快捷方法为:在包上右键-new-(往下找)Create new servlet】
@WebServlet("/req3")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
获取请求体:请求参数
获取字符输入流
BufferedReader reader = req.getReader();
String s = reader.readLine();
System.out.println(s);
}
}
注意:BufferedReader流是通过request对象来获取的,当请求完成后request对象就会被销毁,
3.启动服务器,通过浏览器访问http://localhost:8081/demo/req3.html,提交表单发送POST请求
4.在控制台查看请求,已经获取到了。
10.2.4 统一获取GET/POST请求参数
由于在获取请求参数时,doGet和doPost的方法中存在大量重复代码,因此在实际使用过程中可以通过这种方式:
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
解决GET请求和POST请求获取请求参数的方式不一样问题。
1.首先,两种请求参数的方式都将获取到例如:username=zhangsan&password=123 形式的请求参数。
2.将获取到的参数进行分割,可以得到若干个键与值
3.将若干键与值存放进Map集合中,此Map集合的泛型为Map<String,String[]>
【因为参数的值可能是一个,也可能有多个,所以Map的值的类型为String数组。 】
方法 | 作用 |
Map<String,String[]> getParameterMap()
|
获取所有参数
Map
集合
|
String[] getParameterValues(String name)
|
根据名称获取参数值(数组)
|
String getParameter(String name)
|
根据名称获取参数值
(
单个值)
|
使用案例:
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
1.获取所有参数的map集合
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
希望得到username:zhangsan这样的格式
System.out.print(key+":");
获取值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value+"");
}
System.out.println();//换行用
}
2.根据key获取参数值,数组
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
3.根据key获取单个参数值
String name = req.getParameter("username");
System.out.println(name);
String pw = req.getParameter("password");
System.out.println(pw);
}
小结:
在实际运用过程中采用request提供的获取请求参数的通用方式来获取请求参数,建议使用“this.doGet(req,resp); ”仅在doGet方法中写一遍处理逻辑即可
10.3 解决请求参数中文乱码
10.3.1 POST请求参数乱码
中文参数乱码的原因:
解决方案:
- 页面设置的编码格式为UTF-8
- 把TOMCAT在获取流数据之前的编码设置为UTF-8 【通过request.setCharacterEncoding("UTF-8")设置编码【UTF-8也可以写成小写】】
案例:
<head>第一步
<meta charset="UTF-8">
<title>Title</title>
</head>
1. 解决乱码: POST getReader()
设置字符输入流的编码,设置的字符集要和页面保持一致
request.setCharacterEncoding("UTF-8");
2. 获取username
String username = request.getParameter("username");
10.3.2 GET请求参数乱码
由于GET请求获取请求参数的方式是request.getQueryString(),它不是通过流的方式获取数据的,因此无法参考POST请求乱码的方式
解决思路:
1.乱码原因:
(1)浏览器通过HTTP协议发送请求和数据给后台服务器(Tomcat)
(2)浏览器在发送HTTP的过程中会对中文数据进行URL编码
(3)在进行URL编码的时候会采用页面<meta>标签指定的UTF-8的方式进行编码,如:张三编码后的结果为%E5%BC%A0%E4%B8%89
(4)后台服务器(Tomcat)接收到%E5%BC%A0%E4%B8%89后会默认按照ISO-8859-1进行URL解码
(5)由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码
2.URL编码和URL解码了解
2.1URL编码过程
(1)将字符串按照编码方式转为二进制
(2)每个字节转为2个16进制数并在前边加上%
方法 | 功能 |
String URLEncoder.encode(
"
需要被编码的内容
"
,
"
字符集
(UTF-8)"
)
| URL编码 |
String
URLDecoder
.
decode
(
"
需要被解码的内容
"
,
"
字符集
(UTF-8)"
)
|
URL
解码
|
案例:
public static void main(String[] args) throws UnsupportedEncodingException {
String username = "张三";
URL编码
String encode = URLEncoder.encode(username, "utf-8"); 打印:%E5%BC%A0%E4%B8%89 89
URL解码
String decode = URLDecoder.decode(encode, "utf-8");打印:张三
String decode = URLDecoder.decode(encode, "ISO-8859-1");打印:`å¼ ä¸ `
System.out.println(decode);
}}
3.解决思路
不论使用何种编码、解码方式,“%E5%BC%A0%E4%B8%89”是相同的,二进制数是相同的,先将乱码转换成字节,由字节转换为“张三”即可。
public static void main(String[] args) throws UnsupportedEncodingException {
String username = "张三";
1. URL编码【浏览器对用户输入的中文进行编码】
String encode = URLEncoder.encode(username, "utf-8");
System.out.println(encode);
2. URL解码【tomcat对浏览器提交的参数进行解码】
String decode = URLDecoder.decode(encode, "ISO-8859-1");
System.out.println(decode);
3. 转换为字节数据,编码【我们解决问题从这里开始】
byte[] bytes = decode.getBytes("ISO-8859-1");
4. 将字节数组转为字符串,解码
String s = new String(bytes, "utf-8");
System.out.println(s);
}
这个过程在我们的运用中可以简化为:
username = new String(
username.getBytes(StandardCharsets.ISO_8859_1),
StandardCharsets.UTF_8);
我们主要学习解题思路,因为Tomcat8.0之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8
10.4 Request请求转发
定义:请求转发(forward)是一种在服务器内部的资源跳转方式。
具体过程:
- 浏览器发送请求给服务器,服务器中对应的资源A接收到请求
- 资源A处理完请求后将请求发给资源B
- 资源B处理完后将结果响应给浏览器
- 请求从资源A到资源B的过程就叫请求转发
示例:
request.getRequestDispatcher("资源B路径").forward(request,response);
方法 | 功能 |
RequestDispatcher getRequestDispatcher(String path) | 该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发 |
void forward(ServletRequest request, ServletResponse response) | RequestDispatcher对象的方法。将请求从Servlet转发到服务器上的另一个资源(Servlet,JSP文件或HTML文件) |
void setAttribute(String name,Object o);
|
存储数据到
request
域【
范围
,
数据是存储在
request
对象】
中
|
Object getAttribute(String name);
|
根据
key
获取值
|
void removeAttribute(String name);
|
Object getAttribute(String name);
|
使用案例:在转发请求之前存放一些数据到request域中,并在另一个Servlet中获取数据
@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
存储数据
request.setAttribute("msg","hello");
请求转发
request.getRequestDispatcher("/req5").forward(request,response);
}}
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
获取数据
Object msg = request.getAttribute("msg");
}
}
1.浏览器地址栏路径不发生变化
【虽然后台从/req4转发到/req5,但是浏览器的地址一直是/req4,未发生变化 】
2.只能转发到当前服务器的内部资源
【不能从一个服务器通过转发访问另一台服务器】
3.一次请求,可以在转发资源间使用request共享数据
【虽然后台从/req5转发到/req6,但是这个只有一次请求】
10.5 Response对象继承体系
我们在学习Response时会尽可能与request对比学习,这样有利于记忆
request对象继承体系 | Response对象继承体系 | ||
ServletRequest接口 | Java提供的请求对象根接口 | ServletResponse | Java提供的响应对象根接口 |
HttpServletRequest接口 | Java提供的对Http协议封装的请求对象接口 | HttpServletResponse 接口 | Java提供的对Http协议封装的响应对象接口 |
RequestFacade类 | Tomcat定义的实现类 | ResponseFacade类 | Tomcat定义的实现类 |
10.6 Response设置响应数据
10.6.1 响应行
响应行由HTTP协议及版本,响应状态码以及状态码的描述组成
方法 | 作用 |
void setStatus(int sc);
|
设置响应状态码
|
10.6.2 响应头
响应头的数据格式为键值对,key:value
方法 | 作用 |
void setHeader(String name,String value);
|
设置响应头键值对
|
10.6.3 响应体
响应体需要通过字符、字节输出流的方式写出到浏览器
方法 | 作用 |
PrintWriter getWriter();
|
获取字符输出流
|
ServletOutputStream getOutputStream();
|
获取字节输出流
|
1.Response响应字符数据
要将字符数据写回到浏览器有如下两个步骤:
- 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
- 通过字符输出流写数据: writer.write("aaa");
@WebServlet("/resp3")
public class responsedemo3 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
防止中文乱码
response.setContentType("text/html;charset=utf-8");
获取字符输出流
PrintWriter writer = response.getWriter();
解析html标签
response.setHeader("content-type","text/html");
writer.write("你好");
writer.write("<h1>bbbb</h1>");
细节:流无需关闭
}}
由于一次请求响应结束后,response对象就会被销毁掉,所以不要手动关闭流。
2.Response响应字节数据
要将字节数据写回到浏览器有如下两个步骤:
- 通过Response对象获取字节输出流:
ServletOutputStream outputStream = resp.getOutputStream(); - 通过字节输出流写数据: outputStream.write(字节数据);
@WebServlet("/resp4")
public class responsedemo4 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
1.读取文件
FileInputStream is = new FileInputStream("e://图像 001.png");
2.获取字节输出流
ServletOutputStream os = response.getOutputStream();
3.完成流的copy
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer))!= -1){
os.write(buffer,0,len);
}
is.close();
}}
以上代码过于繁琐,简化方法如下
1.在pom.xml添加依赖 commons-io,如
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2.调用工具类方法
@WebServlet("/resp4")
public class responsedemo4 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
1.读取文件
FileInputStream is = new FileInputStream("e://图像 001.png");
2.获取字节输出流
ServletOutputStream os = response.getOutputStream();
3.调用工具类方法
IOUtils.copy(is,os);
is.close();
}}
10.7 Respones请求重定向
定义:Response重定向(redirect):一种资源跳转方式。
Request请求转发 | Respones请求重定向 | |
定义 | 请求转发(forward)是一种在服务器内部的资源跳转方式。 | 重定向(redirect)是一种资源跳转方式。 |
过程 | (1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求 | (1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求 (2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的 路径 (3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B (4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向 |
实现方式 | request.getRequestDispatcher("资源B路径").forward(request,response); | response.setStatus(302); response.setHeader("location","资源B的访问路径"); |
使用案例:设置重定向状态码到响应头中,重定向到另一个Servlet中获取数据
@WebServlet("/resp1")
public class responsedemo1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
重定向
1.设置响应状态码 302
response.setStatus(302);
2.设置响应头 Location 不区分大小写 要加虚拟目录名称
response.setHeader("location","/tomcatdemo/resp2");
以上可以化简为
response.sendRedirect("/tomcatdemo/resp2");
动态获取虚拟目录,最终化简为如下:
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/resp2");
}
}
Request请求转发 | Respones请求重定向 | |
特点 | 1.浏览器地址栏路径不发生变化 | 1.浏览器地址栏路径发生变化 【当进行重定向访问的时候,由于是由浏览器发送的两次请求,所以地址会发生变化】 |
2.只能转发到当前服务器的内部资源 | 2.可以重定向到任何位置的资源(服务内容、外部均可) 【因为第一次响应结果中包含了浏览器下次要跳转的路径,所以这个路径是可以任意位置资源。】 | |
3.一次请求,可以在转发资源间使用request共享数据 | 3.两次请求,不能在多个资源使用request共享数据【因为浏览器发送了两次请求,是两个不同的request对象,就无法通过request对象进行共享数
据
】
|
根据请求转发和请求重定向的特点,具体问题具体分析,选择更合适的方式处理请求。
10.8 SqlSessionFactory工具类抽取
由于需要使用Mybatis来完成数据库的操作,在每一个Servlet中都写一遍:
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
显然是不行的,重复代码不利于后期的维护 ,SqlSessionFactory工厂类进行重复创建使资源消耗非常严重
解决方案:
- 代码重复可以抽取工具类
- 对指定代码只需要执行一次可以使用静态代码块
public class SqlSessionFactoryUtils {
//想在一个代码块中使用另一个代码块的局部变量,就要这样提升作用域
private static SqlSessionFactory sqlSessionFactory;
//写入静态代码块后就只会执行一次了,因为不能抛出异常所以用try/catch
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 赋值
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}}
public static SqlSessionFactory getSqlSessionFactory(){
// 赋值了之后可以将其返回
return sqlSessionFactory;
}}
从此可以使用一下代码替换那三行代码
SqlSessionFactory sqlSessionFactory =SqlSessionFactoryUtils.getSqlSessionFactory();