1.初识HTTP
1.什么是HTTP?
HTTP:Hyper Text Transfer Protocol (超文本传输协议) 是一个简单的请求-响应协议(应用层协议),它通常运行在 TCP 之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。HTTP的工作原理是基于客户/服务器模式,且面向连接的。
特点:
2.协议分析-发展
3.Method:
| GET | 请求一个指定资源的表现形式,GET请求只能被用于获取数据 |
|---|---|
| POST | 用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用 |
| PUT | 用请求有效载荷替换目标资源的所有当前表示 |
| DELETE | 删除指定资源 |
| HEAD | 请求一个与GET请求的响应相同的响应,但没有响应体 |
| CONNECT | 建立一个到由目标资源标识的服务器的隧道 |
| OPTIONS | 用于描述目标资源的通信选项 |
| TRACE | 沿着到目标资源的路径执行一个消息环回测试 |
| PATCH | 用于对资源应用部分修改 |
safe(安全的):不会修改服务器数据的方法 [GET/ HEAD / OPTIONS]
Idempotent(幂等):同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的,所有safe的方法都是Idempotent的 [GET / HEAD / OPTIONS / PUT / DELETE]
4.状态码
状态码主要分为五大类:
| 1xx | 指示信息,表示请求已接受,继续处理 |
|---|---|
| 2xx | 成功,表示请求已被成功接收、理解、接受 |
| 3xx | 重定向,要完成请求必须进行更进一步的操作 |
| 4xx | 客户端错误,请求有语法错误或请求无法实现 |
| 5xx | 服务器端错误,服务器未能实现合法的请求 |
每一个大类下面又会有若干小类,下面罗列了一些常用的状态码:
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - []:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
301 :资源被永久转移到其他 URL
302 :临时跳转
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - []:表示用户没有权限(令牌、用户名、密码错误),请求未经授权。
403 Forbidden - [] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - []:用户发出的请求针对的是不存在的记录,可能是输错了 URL,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器内部发生错误
504 Gateway Timeout :请求超时
5.协议分析-报文
1.RESTful API:一种API 设计风格;REST - Representational State Transfer
- 每一个URL代表一种资源(面向资源)
- 客户端与服务器之间,传递这种资源的某种表现层
- 客户端通过HTTP method,对服务器端资源进行操作,实现"表现层状态转化"
补充:
**1.提示:REST并没有一个明确的标准,而更像是一种设计风格,**满足这种设计风格的程序或接口我们称之为RESTful(从单词字面来看就是一个形容词)。所以RESTful API 就是满足REST架构风格的接口。
2.REST架构特征:
- 以资源为基础
- 统一接口
- URL指向资源
- 无状态
3.RESTful 6大原则:
- 客户端-服务端(Client-Server)
- 无状态(Stateless)
- 可缓存性(Cacheability)
- 统一接口(Uniform Interface)
- 分层系统(Layered System)
- 按需代码(Code-On-Demand,可选)
2.常用请求头
3.常用响应头
4.缓存
强缓存:
- Expires , 时间戳
- Cache-Control
- 可缓存性
- no-cache :协商缓存验证
- no-store :不使用任何缓存
- 到期
- max-age:单位是秒,存储的最大周期(最大生存周期),相对于请求的时间
- 重新验证*重新加载
- must-revalidate:一旦资源过期,在成功向原始服务器验证之前,不能使用
- 可缓存性
协商缓存:
- **Etag/if-None-Match:**资源的特定版本的标识符,类似于指纹(基本上能看见的Etag都是字符串形式)
- **Last-Modified/If-Modified-Since:**最后修改时间
5.cookie
HTTP请求和cookie的交互流程
响应头字段 Set-Cookie - response
服务器可以通过响应头 Set-Cookie 向客户端发送 cookie
请求头字段 Cookie - request
浏览器发起请求时,匹配出符合条件的所有 cookie 数据,使用 ; 号将所有 = 格式的cookie 拼接起来,放在 Cookie 头中,发送给服务端
📐浏览器匹配 cookie 的算法:
1. 确认是否存在 HttpOnly 属性,存在的话匹配请求域名和 Domain 属性相同。如果不存在 HttpOnly 属性,则请求域名要和 Domain 属性匹配(请求域名是 Domain 属性的子域名或和 Domain 属性相同)
2. 请求 URL 中的路径是 Path 属性的子文件夹或者和Path 属性相同
3. 如果存在 Secure 属性,则当前请求需要是 https 请求
4. 将所有匹配出来的 cookie 排序,然后序列比为最终的 Cookie 字段值
📋补充:
- 为什么叫做cookie呢?
Cookie 这个词来源于计算机编程里的术语" Magic Cookie ",意思是不透明的数据,并不是"小甜饼"的含义(虽然字面意如此)。
- 早期 Cookie 直接就是磁盘上的一些小文本文件,现在基本上都是以数据库记录的形式存放的(通常使用的是 Sqlite )。浏览器对 Cookie 的数量和大小也都有限制,不允许无限存储,一般总大小不能超过4K,这就会存在一个缺陷,对于复杂的存储需求来说肯定是不够用的啦~
- 如果不指定 Expires 或 Max - Age 属性(缓存时间),那么 Cookie 仅在浏览器运行时有效,一旦浏览器关闭就会失效,这被称为**会话 Cookie ( session cookie )**或内存 Cookie ( in - memory cookie ),在 Chrome 里过期时间会显示为" Session "或" N / A "
- 历史上还有" Set -Cookie2"和"Cookie2"这样的字段,但是现在已经不再使用了。
6.HTTP/2概述:更快、更稳定、更简单
帧(frame):HTTP/2通信的最小单位,每个帧都包含帧头,至少也会标识出当前帧所属的数据流
消息:与逻辑请求或响应消息对应的完整的一系列帧
数据流:已建立的连接内的双向字节流,可以承载一条或多条消息。为了防止两端流ID冲突,客户端发起的流具有奇数ID,服务器端发起的流具有偶数ID。
特点:
- 二进制分帧
- 多路复用 (Multiplexing) / 连接共享
- 服务器推送
- 头部压缩
- 请求优先级
特性:
- HTTP/2连接都是永久的,而且仅需每个来源一个连接(复用性)
- 控制流:阻止发送方向接收方发送大量数据的机制(举例:播放视频时,暂停了,浏览器可以智能的考虑说目前暂时不会继续观看,就会阻止发送方继续把后续视频数据发送过来,而是会把当前资源利用去处理优先级更高的事情)
- 服务器推送(使用根据实际情况选择)
❗注意:
- 推送遵循同源策略
- 这种服务器的推送是基于客户端的请求响应来确定的
当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 PUSH_PROMISE 的 Frame,里面带了 PUSH 需要新建的 Stream ID。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH_PROMISE 类型,便会准备接收服务端要推送的流。
HTTP1.X和HTTP2.0在传输数据时的区别:
结论:从图中对比可知,明显HTTP2的信道利用率更高
7.HTTPS概述
- HTTPS:Hypertext Transfer Protocol Secure
- 经过TSL/SSL加密
对称加密和非对称加密:
- 加密和解密都使用同一个密钥,有流式、分组两种
如上图所示,这种方式属于对称加密,双方拥有相同的密钥,信息得到安全传输,但此种方式的缺点是:
- 不同的客户端、服务器数量庞大,所以双方都需要维护大量的密钥,成本很高
- 因每个客户端、服务器的安全级别不同,密钥极易泄露
- 加密和解密需要使用两个不同的密钥:公钥(public key)和私钥(private key)
如上图所示,客户端用公钥对请求内容加密,服务器使用私钥对内容解密,反之亦然,但上述过程也存在缺点:
2.HTTP协议应用场景分析
1.场景分析-静态资源
静态资源方案:缓存 + CDN + 文件名hash(文件内容有变化时,hash就会更新)
2.场景分析-登录
1.什么是跨域?
URL组成:scheme + host name + port
上述三个组成部分,只要有一个不同就会被视为跨域(cross-origin)
2.跨域解决方案:
CORS(cross - origin resource sharing)
预请求:获知服务端是否允许该资源请求(复杂请求)
相关协议头:
- ·Access-Control-Allow-Origin
- Access-Control-Expose-Headers
- Access-Control-Max-Age
- Access-Control-Allow-Credentials
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
- Access-Control-Request-Method
- Access-Control-Request-Headers
- Origin
代理服务器
- 同源策略是浏览器的安全策略,不是HTTP的
Iframe
3.鉴权
下一次进入页面会记住登录态是因为鉴权
- Session + cookie
- JWT(JSON web token)
4.如何避免重复登陆?
3.HTTP协议实战分析
1.AJAX之XHR
- XHR: XMLHttpRequest
- readyState | 0 | UNSENT | 代理被创建,但尚未调用open()方法 | | --- | --- | --- | | 1 | OPENED | open() 方法已经被调用 | | 2 | HEADERS_RECEIVED | send() 方法已经被调用,并且头部和状态已经可获得 | | 3 | LOADING | 下载中;responseText 属性已经包含部分数据 | | 4 | DONE | 下载操作已完成 |
2.AJAX之Fetch
- XMLHttpRequest的升级版
- 使用Promise
- 模块化设计,Respone、Request、Header 对象
- 通过数据流处理对象,支持分块读取
postData('http://example.com/answer',{answer:11})
.then(data => console.log(data))//JSON from `response.json()` call
.catch(error => console.error(error))
//postData 函数使用了 Fetch API 来发送请求
//并通过一系列配置选项来设置请求的参数
function postData(url,data){
//*
return fetch(url,{
body:JSON.stringify(data),
cache:'no-cache',
credentials:'same-origin',
headers: {
'user-agent':'Mozilla/4.0 MDN Example',
'content-type':'application/json'
},
method:'POST',
mode:'cors',
redirect:'follow',
referrer:'no-referrer',
}).then(response => response.json())
}
Node中发起请求的方式:
下面将介绍7种方式并给出示例,出于测试目的,所有示例使用 JSONPlaceholder 假的todo REST API。 我们的REST API返回以下JSON响应
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
- HTTP - 标准HTTP库:这种解决方案与其他方案相比不是很友好
const https = require('https');
https.get('https://jsonplaceholder.typicode.com/todos/1', (response) => {
let todo = '';
// called when a data chunk is received.
response.on('data', (chunk) => {
todo += chunk;
});
// called when the complete response is received.
response.on('end', () => {
console.log(JSON.parse(todo).title);
});
}).on("error", (error) => {
console.log("Error: " + error.message);
});
- Request:它在社区中非常流行,并且被认为是Node.js项目的HTTP客户端
const request = require('request');
request('https://jsonplaceholder.typicode.com/todos/1', { json: true }, (err, res, body) => {
if (err) {
return console.log(err);
}
console.log(body.id);
console.log(body.title);
});
- Needle:是Node.js的可流HTTP客户端,它支持proxy , iconv , cookie , deflate and multi-part
const needle = require('needle');
needle.get('https://jsonplaceholder.typicode.com/todos/1', {json: true}, (err, res) => {
if (err) {
return console.log(err);
}
let todo = res.body;
console.log(todo.id);
console.log(todo.title);
});
从2.0.x版开始,Needle还支持 **Promises **。 对于编写涉及一系列事件的更复杂的代码,Promises是很好的。 上面的代码片段可以使用 Promises 编写,如下所示:
const needle = require('needle');
needle('get', 'https://jsonplaceholder.typicode.com/todos/1', { json: true })
.then(res => {
let todo = res.body;
console.log(todo.id);
console.log(todo.title);
}).catch(err => {
console.log(err);
});
- axios : axios 是基于浏览器 和 Node.js 的基于 Promise 的HTTP客户端,和 Needle 不同的是,axios会自动将响应数据转换为 JSON 对象。
const axios = require('axios');
axios.get('https://jsonplaceholder.typicode.com/todos/1')
.then(res => {
console.log(res.data.id);
console.log(res.data.title);
})
.catch(err => {
console.log(err);
});
- SuperAgent:这是一种和 axios 类似的流行的 HTTP 库,用于在 Node.js 和浏览器中发出 Ajax 请求,和 axios 一样,它也会将响应数据解析为 JSON
const superagent = require('superagent');
superagent.get('https://jsonplaceholder.typicode.com/todos/1')
.end((err, res) => {
if (err) {
return console.log(err);
}
console.log(res.body.id);
console.log(res.body.title);
});
❗提示:SuperAgent可通过插件实现高度扩展。 SuperAgent有许多插件可以执行不同的任务,例如不进行缓存,URL前缀和后缀等。您可以轻松编写自己的插件来扩展SuperAgent的功能
- Got:这是 Node.js 另一个用户友好的轻量级的 HTTP 请求库
//Got 也支持 Promises
const got = require('got');
got('https://jsonplaceholder.typicode.com/todos/1', { json: true })
.then(res => {
console.log(res.body.id);
console.log(res.body.title);
}).catch(err => {
console.log(err.response.body);
});
- Node - fetch :一个轻量级的 HTTP 请求库,它将浏览器中的 Fetch API 功能引入了 Node.js
//Node - fetch 也支持 Promises
const fetch = require('node-fetch');
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json()) // expecting a json response
.then(json => {
console.log(json.id);
console.log(json.title);
})
.catch(err => {
console.log(err);
});
3.标准库:HTTP/HTTPS
4.常用的请求库:axios
5.用户体验
- 网络优化(速度加快)
- 稳定性
6.拓展
1.通信方式
WebSocket
WebSocket出现后,浏览器具备了实时双向通信的能力
- 浏览器与服务器进行全双工通讯的网络技术
- 典型场景:实时性要求高,例如聊天室
- URL使用 ws:// 或 wss:// 等开头
WebSocket协议 与 HTTP 协议对比,有哪些优点?
- 支持双向通信,实时性更强
- 更好的二进制支持
- 较少的控制开销:连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部。
- 支持扩展:ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)
🔗学习资料推荐:WebSocket协议:5分钟从入门到精通
2.QUIC
QUIC: Quick UDP Internet Connection
简介:QUIC 是一种新的互联网传输协议,由谷歌开发。 QUIC 解决了现代 Web 应用程序遇到的许多传输层和应用程序层问题,同时几乎不需要应用程序编写者进行更改。 QUIC 与 TCP+TLS+HTTP2 非常相似,但在 UDP 之上实现。 将 QUIC 作为一个独立的协议可以实现现有协议无法实现的创新,因为它们会受到遗留客户端和中间件的阻碍。
🔗学习资料推荐:QUIC协议原理详解
4.学习小结
通过这次学习,整理笔记发现,HTTP其实是一个很庞大的体系,有很多细化的知识点,以前很多没有关注到的知识点这次也去了解,都还只是对大概有了一个认识,要深入学习透彻后面还是得下功夫的,不过通过实践, 对于浏览器中涉及的请求中的缓存策略展开具体分析后,发现对网络协议了解的更清晰了,果然还是实践出真知!敲重点:动手做!🔨