HTTP
目标:
- 了解 JavaWeb 开发的技术栈;
- 理解 HTTP 协议和 HTTP 请求与响应的数据格式;
- 掌握 Tomcat 的使用
- 掌握在 IDEA 中使用 Tomcat 插件
# Web、JavaWeb 回顾
# Web、JavaWeb 概念
Web:全球广域网,也称为万维网(WWW),简单说就是能够通过浏览器访问的网站。
JavaWeb:JavaWeb 就是利用 Java 技术来解决 Web 互联网领域相关问题的技术栈,简单说就是使用 Java 技术开发网站。
# JavaWeb 相关名词介绍
# B/S 结构 (Browser/Server,浏览器/服务器模式)
B/S 结构是 WEB 兴起后的一种网络结构模式,再介绍 B/S 结构前,我们先了解一下另外一种结构--C/S。
C/S:Client/Server,客户端/服务器模式,服务器通常采用高性能的 PC、工作中或小型机,并安装数据库管理系统,而且客户端需要安装专用的客户端软件才能使用。
C/S结构的优点:
- C/S 架构的界面和操作可以很丰富,客户端操作界面可以随意排列,满足客户的需要。
- 安全性可以很容易保证,因为只有两层传输,而不是中间有很多层。
- 只有一层交互,响应数据较快,直接相连,中间没有什么组个或岔路。
C/S结构的缺点:
- 适用面窄,通常用于局域网中。
- 用户群固定,需要客户端程序才可使用。
- 维护成本高,一次升级,所有客户端程序都需要改变。
B/S:客户机上只要安装一个浏览器,如谷歌、火狐、360 等,服务器分为 Web 应用服务器和数据库服务器,在这种结构下,用户界面通过浏览器展现,一部分业务逻辑在前端页面实现,主要的业务逻辑在服务器端实现。浏览器通过 Web 应用服务器与数据库进行交互。
B/S结构的优点:
- 客户端无需安装,有 Web 浏览器即可。
- B/S 结构可以放在广域网上,通过一定的权限控制实现多用户访问。
- B/S 结构无需升级多个客户端,升级服务器即可,可以随时更新版本。
B/S结构的缺点:
- 跨浏览器上,B/S 结构不尽如人意,需要花费精力解决浏览器兼容性问题。
- 界面表现要达到 C/S 的程度,同样需要花费不少精力。
- 在性能和安全性上需要花费巨大的设计成本。
# 静态网站
- 什么是静态网站?
静态网站由存储在 Web 服务器上的固定数量的预构建文件组成。这些文件由HTML、CSS和JavaScript编写,这些文件叫做静态资源文件,当用户想服务器请求页面时,服务器会返回指定的 HTML 文件以及 HTML 文件引用的 CSS、JavaScript 文件。
在此交换过程中,Web 服务器将文件发送给用户之前不会更改文件,因此网页对于请求它的人来说都是完全相同的。对所有人来说,看到的内容是固定不变的,静态的,--改变网站内容的唯一方法是手动更改文件的内容。简单说静态网站对每个人来说总是一样的。
# 动态网站
与所有人看到的内容总是一样的静态网站不同,动态网站向不同访问者显示不同的信息。访问者看到的内容可以由不同的因素决定,例如他们的位置、当地时间、设置和偏好等等。
为了在前端页面上实现更大的灵活性,动态网站需要再后端增加更多的复杂性。这些网站不会将每个页面存储为固定的 HTML 文件。相反,Web 服务器会即时构建页面--当用户请求页面时,服务器从数据库中提取信息并为客户构建定制的 HTML 文件,页面构建完成后,再将页面发送给用户的浏览器。
为了在后端构建页面,除使用HTML、CSS、JavaScript之外,动态网站还需要使用服务器端语言,比如 Java。根据用户构建页面的过程可能非常复杂,但是用户看不到这个过程,只能看到浏览器加载的网页与静态站点相同。
实际上,我们接下来的课程就是教大家如何使用 JavaWeb 技术来开发动态网站。
# 数据库
上面我们介绍了静态网站、动态网站,接下来我们结合之前学习的数据库来分析一下动态网站的访问过程:
- 浏览器发送请求到服务器,访问所需要的相关资源;
- 资源分为动态资源和静态资源,动态资源是使用 Java 代码按照 Servlet 和 JSP 的规范编写的内容;
- 在 Java 代码中从数据库读取数据,进行业务逻辑处理;
- 获取数据后,依据数据动态生成 HTML 页面,并结合 CSS 和 JavaScript 使展示效果更好;
- 服务器将构建好的页面响应给浏览器;
- 浏览器解析这些资源;
- 解析后将效果展示在浏览器中,用户就可以看到最终的结果。
在整个 Web 的访问过程中,会设计到很多技术,这些技术有的已经学习过,有的是我们后面的课程要学些的。
# HTTP 协议
上面介绍了 Web 的访问过程,接下来我们思考一个问题:
用户打开一个浏览器,输入网址,向服务器发送数据,那么这个数据是怎么发送出去的呢?
加入每个 Web 网站都有自己发送数据的规则,整个互联网是不是就乱套了,而且用户访问也不是很方便,每个网站都要开发属于自己的客户端浏览器软件,这就从 B/S 结构变为了 C/S 结构。
所以,必须要有一个统一的规则,让大家发送数据或接收数据有一个依据,于是,HTTP 协议应运而生。
HTTP 协议:Hyper Text Transfer Protocol 主要定义浏览器和服务器之间的通信规则,浏览器发送请求给服务器,服务器响应数据给浏览器,这整个过程都要遵守一定的规则,和之前我们学习的 TCP、UDP 一样,HTTP 也是一种通信规则,只不过 TCP、UDP 相对来说更底层。
# Web 服务器
上面我们知道用户通过浏览器以 HTTP 协议发送请求和数据,那服务器上就需要有一个软件来根据 HTTP 洗衣解析请求和数据,然后把处理的结果在按照 HTTP 协议返回给浏览器。这个软件就是 Web 服务器软件。简单说,Web 服务器就是用来解析 HTTP 协议,解析请求数据并发送响应数据给浏览器的。
Web 服务器软件有很多,我们后面会重点学习一款目前最为常用的Tomcat服务器。
# Web 核心课程安排
下面我们再来介绍下 Web 核心课程的安排,Web 核心课程计划有 4-5 天的学习内容:分别是:
- 第一天:HTTP、Tomcat、Servlet,在学习 Servlet 时,我们会先简单了解一下 HTML 相关的内容
- 第二天:Request(请求)、Response(响应)
- 第三天:JSP
- 第四天:会话技术(Cookie、Session)
- 第五天:Filter(过滤器)、Listener(监听器)
- Request 就是从客户端想服务器端发出的请求对象;
- Response 是从服务端响应给客户端的结果对象;
- JSP 是动态网页技术;
- 会话技术是用来存储客户端和服务端交互所产生的数据;
- 过滤器是用来拦截客户端的请求;
- 监听器是用来监听特定事件;
上面的技术该如何使用,我们会逐个进行详细的讲解,下面我们先来学习下 HTTP、Tomcat 和 Servlet。
# HTTP 协议
# HTTP 协议简介
HTTP 协议是 Hyper Text Transfer Protocol (超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web)服务器与本地浏览器之间传输超文本(文本文件、图片、音视频等)的传送协议。
HTTP 于 1990 年提出,经过几年的使用与发展,得到不断地完善和扩展。它工作与 B/S 架构之上,浏览器作为 HTTP 客户端通过 URL 想 HTTP 服务端即 Web 服务器发送请求,Web 服务器根据收到的请求,向客户端发送响应信息。
使用 HTTP 协议,每当有新的请求发送时,就会有对应的新响应产生,HTTP 协议本身并不保留之前一切的请求或响应的报文信息。这是为了更快地处理大量请求,确保协议的可伸缩性,而特意把 HTTP 设计成这么简单的。
可是,随着 Web 的不断发展,因为无状态会导致我们的业务处理变得非常棘手,比如,用户登录京东,即使他跳转到京东的其他页面后,也需要能继续保持登录状态。针对这个问题,网站为了能够掌握是谁发送的请求,需要保存用户的状态。HTTP 虽然是无状态协议,但为了实现期望的保持状态功能,就引入了 Cookie、Session 技术。有了它们再用 HTTP 协议通信,就可以管理状态了,有关 Cookie、Session 的内容我们后面再进行讲解。
小结:什么是 HTTP 协议?
- 超文本传输协议,规定了浏览器与服务器之间数据交互的格式;
- 将浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 Web 服务器发送请求;
- 如果服务端不遵循该协议,那就需要自己开发一个客户端,让用户下载,然后通过下载的客户端进行访问,否则,用户通过浏览器是无法访问服务端的应用的。
# HTTP 协议四大特性
- HTTP 是协议基于 TCP/IP 协议之上的应用层协议
HTTP 协议的传输过程就是这样通过协议栈逐层向下,每一层都添加本层的专有数据,层层打包,然后通过下层发送出去。接收数据则是相反的操作,从下往上穿过协议栈,逐层拆包,每层去掉本层的专有头,上层就会拿到自己的数据。
可以把 HTTP 利用 TCP/IP 协议栈传输数据想象成一个收发快递的过程。
假设你想把意见礼物送给朋友,你需要将这个礼物先包装一下,这件礼物就相当于 HTTP 协议要传输的内容,比如 HTML,然后 HTTP 协议为它加一个 HTTP 专用附加数据。你把礼物教给快递小哥,为了保护货物和记录收件人信息,快递小哥又加了层包装再贴了个标签,这就相当于在 TCP 层给数据在此打包,加上了 TCP 的头信息。
接着快递小哥将快递运到快递集散点,然后装进更大的物流车里,相当于在 IP 层、MAC 层对 TCP 数据加上了 IP 头、MAC 头。之后经过漫长的运输,快递到达目的地,要卸货再放进另一位快递小哥的车里,就是在 IP 层、MAC 层传输后拆包。快递员送货到家后签收撕掉标签,去掉了 TCP 层的头,你朋友再拆掉礼物包装,也就是 HTTP 头,最后就拿到了礼物,也就是真正的 HTTP 页面。
这个比喻省略了很多 TCP/IP 协议里的细节,比如建立连接、路由、数据切分与重组、错误检查等,但核心的数据传输过程是类似的。
- HTTP 协议基于请求-响应模式
HTTP 协议规定:请求从客户端发出,最后服务端响应客户端请求并返回。
也就是说,用户访问数据库先从客户端开始建立通信,服务端在没收到请求之前不会发送数据并响应客户端。
- 无状态保存:不保存用户信息状态
HTTP 是一种不保存状态,即无状态(stateless)协议。HTTP 协议自身不对请求和响应之间的通信状态进行保存。也就是说仅仅在 HTTP 这个级别,协议对于发送过的请求或响应都不做持久化处理。
总结:媳妇虐我千百遍,我待媳妇如初恋。
使用 HTTP 协议,每当有新的请求发送时,就会有对应的新响应产生,但协议本身并不保留之前一切的请求或响应报文。
优点:为了更快地处理大量请求,确保协议的可伸缩性。
问题:但这样也产生了一些比较棘手的问题:比如,用户登录一家购物网站,即使他跳转到该站的其他页面也需要能继续保持登录状态。针对这个示例,网站为了能够掌握是谁发出的请求,就需要保存用户的状态。怎么实现?
解决:HTTP 协议虽然是无状态协议,但为了实现期望的保持状态功能,就引入了Cookie 技术,有了 Cookie 再用 HTTP 协议通信就可以管理状态了。
- 短连接
短连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。即:客户端请求一次就响应一次,之后就没有任何关系。
# HTTP 请求协议
HTTP 协议规定了客户端与服务端之间的通信格式,它包含由浏览器发送数据到服务器需要遵循的请求协议与服务器发送数据到浏览器需要遵循的响应协议。
接下来,咱们看下 HTTP 协议是怎么规定消息格式的。
为了更直观的演示,大家将我们课程资料里面的http
目录拷贝到我们的 IDEA 工作目录中,比如,D:\itcast\Source\itcast\JavaWeb
然后,一路next
即可。这个模块中有一个Server.java
类,它自定义了一个服务器代码来模拟接收浏览器请求,主要使用的是咱们之前学的ServerSocket
和Socket
。
package com.itheima;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* 自定义服务器
* @author sunyy
* @version 0.0.1
* @since 2022.10.25
*/
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888); // 监听指定端口
System.out.println("server is running...");
while (true){
Socket sock = ss.accept();
System.out.println("connected from " + sock.getRemoteSocketAddress());
Thread t = new Handler(sock);
t.start();
}
}
}
class Handler extends Thread {
Socket sock;
public Handler(Socket sock) {
this.sock = sock;
}
public void run() {
try (InputStream input = this.sock.getInputStream()) {
try (OutputStream output = this.sock.getOutputStream()) {
handle(input, output);
}
} catch (Exception e) {
try {
this.sock.close();
} catch (IOException ioe) {
}
System.out.println("client disconnected.");
}
}
private void handle(InputStream input, OutputStream output) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
// 读取HTTP请求:
boolean requestOk = false;
String first = reader.readLine();
if (first.startsWith("GET / HTTP/1.")) {
System.out.println(first);
requestOk = true;
}
for (;;) {
String header = reader.readLine();
if (header.isEmpty()) { // 读取到空行时, HTTP Header读取完毕
break;
}
System.out.println(header);
}
System.out.println(requestOk ? "Response OK" : "Response Error");
if (!requestOk) {
// 发送错误响应:
writer.write("HTTP/1.0 404 Not Found\r\n");
writer.write("Content-Length: 0\r\n");
writer.write("\r\n");
writer.flush();
} else {
// 发送成功响应:
//读取html文件,转换为字符串
BufferedReader br = new BufferedReader(new FileReader("http/html/a.html"));
StringBuilder data = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null){
data.append(line);
}
br.close();
int length = data.toString().getBytes(StandardCharsets.UTF_8).length;
writer.write("HTTP/1.1 200 OK\r\n");
writer.write("Connection: keep-alive\r\n");
writer.write("Content-Type: text/html\r\n");
writer.write("Content-Length: " + length + "\r\n");
writer.write("\r\n"); // 空行标识Header和Body的分隔
writer.write(data.toString());
writer.flush();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
上面的代码,我们不需要自己写,主要是通过上述代码,让大家了解到服务器可以使用 Java 编写,并且可以接收页面发送的请求和响应数据给前端浏览器,指正用到 Web 服务器时,我们使用目前比较流程的 Web 服务器即可,如,Tomcat。
下面来运行一下我们服务器端的程序,通过浏览器访问http://localhost:8888来看一下服务器控制台的输出信息:
大家可以在控制台看到服务器端收到的请求信息的内容大概是什么样子的。控制台输出的内容即为 HTTP 报文,简单的说用于 HTTP 协议交互的信息被称为 HTTP 报文,它分为请求报文和响应报文,顾名思义,请求报文即为浏览器发起的报文,响应报文为服务器端响应的报文。HTTP 报文本身是由多行数据构成的字符串文本。
下面咱们详细了解一下请求报文和响应报文
请求报文:
- 格式
第一行:HTTP 请求报文中的第一行数据,包含三块内容,分别是请求方式、请求 URL 路径、HTTP 协议及版本,请求方式总共有七种,我们最常用的就是 GET 和 POST;
第二行:第二行开始为请求头,格式为key:value
的形式,请求头中会包含若干个属性,常见的 HTTP 请求头有:
Host: 表示请求的主机名
User-Agent: 浏览器版本,如Chrome浏览器的标识类似于Mozilla/5.0 ... Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...) like Gecko;
Accept: 表示浏览器能接收的资源类型,如text/*,image/*或者*/*;
Accept-Language: 表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
Accept-Encoding: 表示浏览器可以支持的压缩类型,例如gzip,deflate等。
2
3
4
5
HTTP 协议定义这些数据有什么用?
服务端可以根据请求头中的内容来获取客户端的相关信息,有了这些信息服务端就可以处理不同的业务需求,比如:
- 不同浏览器解析 HTML 和 CSS 标签的结果会有不一致的地方,因此就会导致相同的代码在不同的浏览器出现不同的结果;
- 服务端可以根据客户端请求头中的数据获取到客户端的浏览器类型,就可以根据不同的浏览器设置不同的代码来达到一致的效果。
- 这就是我们常说的浏览器兼容性问题。
第三行:为请求体,但并不是所有的请求都有请求体,有无请求体与请求方式有关,GET请求无请求体,POST请求有,下面就来介绍一下这两种常用的请求方式:
GET:向服务端索取数据(例如,输入网址获取对应的内容)
POST:向服务端提交数据(例如,登录,输入用户名、密码,提交到服务端进行校验)
GET和POST的区别:
- 都可以携带额外的参数:
- GET 提交的数据会放在 URL 之后,以
?
分割 URL 和参数,参数之间以&
相连 - POST 方法是把提交的数据放在 HTTP 包的请求体(Body)中
- GET 提交的数据会放在 URL 之后,以
- 提交数据的大小限制:浏览器对 URL 长度有限制,所以 GET 提交的数据大小有限制,而 POST 方法则没有
- 数据的安全性:GET 方式提交数据,会带来安全问题,比如一个登录页面,通过 GET 方式提交数据时,用户名和密码将出现在 URL 上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录中获得该用户的账号和密码
接下来我们以一个登录的简单例子来看下效果,在 IDEA 中新建一个 HTML 文件,输入如下内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>GET、POST演示</title>
</head>
<body>
<form action="#" method="get">
用户名:<input type="text" name="username" /> 密码:<input
type="password"
name="password"
/>
<input type="submit" value="提交" />
</form>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
首先,以GET方式提交数据看一下效果:
接着,将method="get"
修改为method="post"
,再次打开页面看一效果,注意一定要重新打开页面。
小结
// 请求首行 : 请求方法, 协议版本...
// 请求头 : 一大堆的 k:v 键值对
// 空行 \r\n : 用来标识作用
// 请求体 : 并不是所有的请求方法都有, 只要用来携带敏感性数据(get没有,post有)
2
3
4
# HTTP 响应协议
介绍完 HTTP 请求,咱们再来看下 HTTP 响应。
服务器收到客户端发送的 HTTP 请求后,根据 HTTP 请求中的动作要求,服务端做出具体的动作,然后将结果响应给客户端,称为 HTTP 响应。
响应格式:
格式说明:
/ 响应首行 : 响应状态码, 协议版本....
// 响应头 : 一大堆 k:v 键值对
// 空行 \r\n : 用来标识作用
// 响应体 : 响应正文, 展示给用户的数据
2
3
4
- HTTP 响应由三部分组成:状态行、响应头、响应正文;
- 状态行:包括协议版本 Versionn、状态码 Status Code、回应短语;
- 响应头(server header):包括搭建服务器的软件,发送响应的时间,回应数据的格式等信息,包含 HTTP 状态码(HTTP Status Code);
- 响应正文:就是响应的具体数据。
响应状态码说明:
HTTP 状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字起分类的作用,不同的状态码代表不同的含义。简单说,就是用较为简单的数字来表示一串中文意思。
- 状态码大类
状态码分类 | 说明 |
---|---|
1xx | 响应中——临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果它已经完成则忽略它 |
2xx | 成功——表示请求已经被成功接收,处理已完成 |
3xx | 重定向——重定向到其它地方:它让客户端再发起一个请求以完成整个处理。 |
4xx | 客户端错误——处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等 |
5xx | 服务器端错误——处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP 版本不支持等 |
- 常见的响应状态码
状态码 | 英文描述 | 解释 |
---|---|---|
200 | OK | 客户端请求成功,即处理成功,这是我们最想看到的状态码 |
302 | Found | 指示所请求的资源已移动到由Location 响应头给定的 URL,浏览器会自动重新访问到这个页面 |
304 | Not Modified | 告诉客户端,你请求的资源至上次取得后,服务端并未更改,你直接用你本地缓存吧。隐式重定向 |
400 | Bad Request | 客户端请求有语法错误,不能被服务器所理解 |
403 | Forbidden | 服务器收到请求,但是拒绝提供服务,比如:没有权限访问相关资源 |
404 | Not Found | 请求资源不存在,一般是 URL 输入有误,或者网站资源被删除了 |
428 | Precondition Required | 服务器要求有条件的请求,告诉客户端要想访问该资源,必须携带特定的请求头 |
429 | Too Many Requests | 太多请求,可以限制客户端请求某个资源的数量,配合 Retry-After(多长时间后可以请求)响应头一起使用 |
431 | Request Header Fields Too Large | 请求头太大,服务器不愿意处理请求,因为它的头部字段太大。请求可以在减少请求头域的大小后重新提交。 |
405 | Method Not Allowed | 请求方式有误,比如应该用 GET 请求方式的资源,用了 POST |
500 | Internal Server Error | 服务器发生不可预期的错误。服务器出异常了,赶紧看日志去吧 |
503 | Service Unavailable | 服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好 |
511 | Network Authentication Required | 客户端需要进行身份验证才能获得网络访问权限 |
# URL 统一资源定位符
介绍HTTP时,我们一直提到URL,那么URL是什么?
URL:Uniform Resource Locator,翻译过来就是统一资源定位符,它是对可以从互联网上得到的资源的位置和访问方法的一种简洁表示,是互联往上标准资源的地址。互联往上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL的格式为:
协议://IP:端口/路径?name=sunyy&age=18
?
之前的是请求路径,?
之后的是请求数据 形式:protocol:[//[user:password@]host[:port]][/]path[?query-string][#anchor]
方框内的是可选部分:
- protocol: 协议,例如:http, https, ftp等
- user:password@:用户的登录名以及密码,一般都不需要
- host:服务器的IP地址或者域名
- port:服务器的端口(http协议默认为80端口,https默认为443)
- path:访问资源的路径
- query-string:参数,它通常使用键值对
- anchor:锚点(跳转到网页的指定位置)