JavaWeb - Note03 HTTP & HttpServlet
HTTP 协议
W3C 制定的一种超文本传输协议(通信协议,传输非普通文本,例如流媒体:声音、视频、图片等)
B(Browser)与 S(Server)之间收发数据都遵循此协议,互不依赖(解耦合)
HTTP 协议包括:请求(request)协议、响应(response)协议
请求协议
包括:请求行、请求头、空白行、请求体
get 请求报文:
1 |
|
post 请求报文:
1 |
|
F12,找到 network,通过这个页面可以查看协议的具体内容响应协议
请求行、请求头在 Header - Request Header 中
请求体在 Header - Form Data 中
请求行
包括三部分:
请求方式(7种)
get
post
delete
put
head
options
trace
URI
- URI:统一资源标识符,代表网络中某个资源的名称(无法定位)
- URL:统一资源定位符,代表网络中某个资源,通过 URL 可以定位到该资源
URL 包括 URI,e.g:
- URL:
http://localhost:8080/servlet/index.html
- URI:
servlet/index.html
协议版本号
请求头
作为了解
- 请求的主机
- 主机的端口
- 浏览器信息
- 平台信息
- cookie 信息
- …
空白行
用来区分请求头和请求体
请求体
向服务器发送的具体数据
get & post
区别
get
发送数据时,在请求行发送数据,发送的数据回显在地址栏
只能发送普通字符串,不同浏览器有不同长度限制(无明确规范)
get 请求绝对安全,只是为了从服务器获取数据,不会对服务器造成威胁(get 请求不应用在敏感信息)
支持缓存
任何一个 get 请求最终的 “响应结果” 都会被浏览器缓存起来
在浏览器缓存当中,一个 get 请求对应一个资源
实际上,只要发送 get 请求,浏览器就会先从本地缓存找,找不到才会去服务器获取。目的是提高用户体验
若不想 get 请求走缓存,也就是希望每次都从服务器获取,可以在路径后加一个时间戳,这样资源名每时每刻都在改变,无法从本地缓存找到
post
发送数据时,在请求体中发送,不会回显在浏览器地址栏
可以发送任何类型数据,包括:普通字符串、流媒体(视频、声音、图片…)等,可以发送大数据量,理论上无长度限制
post 请求有危险,向服务器提交数据,数据可能有危险,所以一般情况下拦截数据,大部分都会监听 post 请求
不支持缓存
目的是给服务器提交数据,返回的是处理结果,缓存无意义
二者发送请求的数据格式相同,只是位置不同
数据会挂在 URL 后面,且 URL 与数据之间会添加一个“?”
http://[url]?name=value&name=value&name=value…
其中,以 form 表单为例:
- name:表单中输入域控件的 name
- value:表单中输入域控件的 value
所有的 value 皆是字符串
发送途径
post:目前为止,form 表单中,method 设置为 post 则为 post
get:除 post 以外,其他一律为 get
如:输入 URL、超链接、form 表单、method 为 get
使用情形
get:
从服务器端获取数据
post:
向服务器端发送数据
大部分 form 表单中填写大量数据
form 表单中存在敏感信息
做文件上传(非普通文本)
响应协议
包括:状态行、响应头、空白行、响应体
具体报文:
1 |
|
状态行、响应头在 Header - Response Header
响应体在 Response
若想响应体好看,可以使用 out.println(),此为源代码换行;网页展示换行需输出
<br>
标签
*状态行
三部分组成:
协议版本号
HTTP/1.1
状态码(HTTP 协议规定的状态号,对应着不同的响应结果)
200 请求响应成功
404 访问的资源不存在(前端错误)、路径错误或服务器资源没有启动成功
405 前端发送的请求方式与后端请求处理方式不一致,例如:
- 前端 post 请求,后端按 get 请求处理
- 前端 get 请求,后端按 post 请求处理
500 服务器端的程序出现异常(后端错误)
4 开头一般为浏览器端错误
5 开头一般为服务器端错误
状态描述信息
- ok 表示正常成功结束
- not found 表示资源找不到
响应头
作为了解
- 响应内容类型
- 响应内容长度
- 响应时间
- …
空白行
分隔响应头和响应体
响应体
响应正文,是一个长的字符串,被浏览器渲染、解释并运行,最终展示效果
模板方法设计模式
在模板类的模板方法中定义核心算法骨架(可以使用 final 修饰,防止修改),具体的实现步骤可以延迟到子类当中完成(可以使用 abstract 关键字)
HttpServlet
jakarta.servlet.http.HttpServlet
HttpServlet 类专门为 HTTP 协议准备,比 GenericServlet 更适合 HTTP 协议下的开发
接口
jakarta.servlet.http.HttpServlet
HTTP 协议专用的 Servlet 抽象类
jakarta.servlet.http.HttpServletRequest
HTTP 协议专用的请求对象(简称 request 对象)
封装了请求协议的全部内容(WEB 服务器解析)
jakarta.servlet.http.HttpServletResponse
HTTP 协议专用的响应对象
源码分析
HttpServlet 源码分析(按生命周期)
1 |
|
若直接继承 HttpServlet,直接重写 HttpServlet 中的 serice 方法可行,但享受不到 HTTP 协议专属服务(如错误代码提示)
未重写 doGet 等处理请求方法,则会执行父类 HttpServlet 中的 doGet 输出错误信息:
1
2
3
4
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}避免错误信息的方法:后端重写什么类型的请求,前端就发送什么类型的请求
最终开发步骤
- 编写一个 Servlet 类,直接继承 HttpServlet
- 重写 doGet/doPost 方法
- 配置 web.xml 文件
- 准备前端页面,form 表单中指定路径即可
web 站点欢迎页面
对于一个 webapp,可以设置它的欢迎页面,当访问这个 webapp 或 访问这个 web 站点时,没有指定任何“资源路径”,这时会访问欢迎页面
之前访问的是:
1 |
|
欢迎页面访问方式:
1 |
|
设置欢迎页面
web 目录下新建一个文件 welcome.html
在 web.xml 中配置
<welcome-file-list>
1
2
3
4
5<welcome-file-list>
<!-- 不需要“/”,且从 webapp 的根目录开始 -->
<welcome-file>welcome.html</welcome-file>
<!-- 可以设置多个,优先级从上往下 -->
</welcome-file-list>启动服务器,地址输入到项目名即可
欢迎页面名为 index 时,可自动访问不需要配置,是因为 Tomcat 已提前配置好
实际上配置欢迎页面有两个方法:
在 webapp 内部的 web.xml 中(局部配置)
在 CATALINA_HOME\conf\web.xml 中(全局配置)
1
2
3
4
5<welcom-file-list>
<welcom-file>index.html</welcom-file>
<welcom-file>index.htm</welcom-file>
<welcom-file>index.jsp</welcom-file>
</welcom-file-list>
欢迎页还可以是一个 Servlet 动态页面,路径直接使用 url-pattern(去掉斜杠)
*HttpServletRequest 接口详解
jakarta.servlet.http.HttpServletReques
该接口是 Servlet 规范中的一员,父接口是 ServletRequest
由 WEB 服务器实现该接口,封装了 HTTP 请求协议。用户发送请求时,遵循了 HTTP 协议,WEB 服务器将 HTTP 协议中的信息及数据全部解析出来,然后 WEB 服务器把这些信息封装到 HttpServletRequest 对象中,传递给 Java 程序员
一次请求对应一个 request 和 response
常用方法
获取用户提交的数据
不建议直接使用 Map 集合存储提交的数据,因为 Map 集合 key 重复则 value 覆盖
采用 Map<String, String[]>
的方式存储
String getParameter(String name)
根据 Key 获取 value 一维数组中的第一个元素
1
2System.out.println(req.getParameter("username")); // vv
System.out.println(req.getParameter("password")); // 123Map<String String[]> getParameterMap()
获取 Map 集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 获取 req Map 集合
Map<String, String[]> parameterMap = req.getParameterMap();
// 获取 keySet
Set<String> parameterKeys = parameterMap.keySet();
for (String key : parameterKeys) {
System.out.print(key + "=");
// 通过 key 遍历数组
for (String value : parameterMap.get(key)) {
System.out.print(value + " ");
}
System.out.println();
}
/*
username=vv
password=123
habit=sing guitar
*/Enumeration<java.lang.String> getParameterNmes()
获取 Map 集合中所有的 Key
1
2
3
4
5
6
7
8// getParameterNames 获取所有的 key
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
System.out.print(parameterNames.nextElement() + " ");
}
/*
username password habit
*/String[] getParameterVlues(String name)
根据 Key 获取 Map 集合的 value
通常在复选框中使用
1
2
3
4
5
6
7
8// getParameterNames 获取所有的 key
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
System.out.print(parameterNames.nextElement() + " ");
}
/*
vv 123 sing guitar
*/
其他常用方法
String getRemoteAddr()
获取客户端地址
1
System.out.println(req.getRemoteAdder()); // 127.0.0.1
void setCharacterEncoding(String env)
设置请求体的字符集,一般用于解决 POST 乱码问题
1
req.setCharacterEncoding("UTF-8");
Tomcat 10 之后,request 请求体中默认为 UTF-8,不需要设置字符集,不会出现乱码问题
Tomcat 9 之前(包括9),如果前端 post 方法提交中文,会出现乱码问题
响应同样会有乱码问题,在 setContentType 中设置字符集
1
resp.setContentType("text/html; charset=UTF-8");
get 请求不会出现乱码,因为
CATALINA_HOME\conf\server.xml
中有个 Connector 标签,其中的 URIEncoding 属性用以配置字符集编码Tomcat 9 之后(包括)默认为 UTF-8 的格式(之前为 ISO-8859-1)
String getContextPath()
获取应用根路径
1
2String contextPath = request.getContextPath();
// /servlet08String getMethod()
获取请求方式
String getRequestURI()
获取请求 URI(带项目名)
String getServletPath()
获取请求 URI(不带项目名)
请求域对象
请求域对象要比应用域对象范围小很多,生命周期也短很多
请求域只在一次请求内有效(HttpServletRequest request 对象)
请求域也有这三个方法:
1 |
|
与应用域选用原则:
- 尽量使用小的域对象,占用资源较少
跨请求域
使用 Servlet 中的转发机制,执行完 A 后跳转到 B,转发为一次跳转
请求转发器:
RequestDispatcher getRequestDispatcher(String path)
请求转发器方法:
- void forward(ServvletRequest req, ServletResponse resp)
请求步骤:
1 |
|
转发的下一步不一定是 Servlet,支持所有 Tomcat 中的合法资源
转发路径以 “/” 开始,不加项目名
易混淆的两个方法
1 |
|
第一个方法:获取用户在浏览器上提交的数据
第二个方法:获取请求域中绑定的数据
Project
资源跳转
在一个 web 应用中通过两种方式,可以完成资源的跳转
- 转发
- 重定向
区别:
代码上
转发
将本 Servlet 中的 request 与 response 通过 foward 方法转发给下一个 Servlet
1
2
3
4
5
6
7// 获取请求转发器对象
RequestDispatcher dispatcher = request.getRequestDispatcher("dept/list");
// 调用请求转发器对象的 forward 方法转发
dispatcher.forward(request, response);
// 合并一行代码
request.getRequestDispatcher("dept/list").forward(request, response);重定向
调用 response 中的 sendRedirect 方法,将路径响应给浏览器,浏览器又自发地向服务器发送了一次全新的请求
第一次请求:servlet/a
第二次请求:servlet/b
1
2// 需要以项目名开始
response.sendRedirect(request.getContextPath() + "/b");
形式上
转发(一次请求)
请求前与请求后浏览器地址栏不变
重定向(两次请求)
请求后浏览器地址栏变成最后一次发送的请求
本质区别:
转发是由 WEB 服务器来控制
重定向是浏览器完成的
如何选择:
- 如果在上一个 Servlet 中向 request 域中绑定了数据,希望从下一个 Servlet 中把 request 域中的数据取出时使用转发
- 剩下所有的请求均使用重定向
保存转发存在刷新问题,若重复刷新,则导致反复提交;重定向刷新的是最后一次请求
Servlet 注解
jakarta.servlet.annotation.WebServlet
分析 oa 项目中的 web.xml 文件
- 配置信息过多,对于大项目体量而言不利
- web.xml 中进行 Servlet 信息配置,开发效率低,每个都需要配置
- 配置文件很少被修改,可以直接写到 Java 类中
Servlet 3.0 之后,推出了各种基于注解式开发
开发优点
- 效率高,不需要编写大量配置信息,直接在 Java 类上使用注解进行标注,简化配置
- web.xml 文件体积变小
一般使用注解+配置文件的开发模式
不会经常变化修改的配置使用注解,可能修改的写到配置文件中
注解属性
name:指定 Servlet 名字,等同于
<servlet-name>
urlPatterns:指定映射路径,可以指定多个 String
若只有一个路径,则大括号可以省略
value:同 urlPatterns
若只有一个路径,则大括号可以省略
如果注解名是 value,则属性名也可以忽略
1
@WebServlet("/wel")
支持模糊匹配:
1
@WebServlet("/dept/*")
loadOnStartup:启动时是否自动加载,int 决定创建对象顺序
initParams:启动时初始化参数
注解对象使用格式:
@注解名(属性名=属性值, 属性名=属性值)
示例代码:
1 |
|