浏览器缓存策略 ⚔️
浏览器的缓存机制主要分为强制缓存与协商缓存。强制缓存通常是在浏览器端判断是否使用缓存,而协商缓存则是在服务器端判断是否使用缓存。
两者是共同工作的,在使用缓存之前,浏览器会先判断强制缓存是否生效🎯,如果强制缓存失效,浏览器会发送请求到服务器,再通过协商缓存来判断是否使用缓存🧐。
强制缓存
强制缓存可以通过响应头中的 Expires
和 Cache-Control
来设置。当浏览器第一次请求资源时,服务器会在响应头中包含相应的缓存策略(就是这俩字段),浏览器会将其记录下来,当再次请求这个资源时,浏览器会根据这些字段来判断是否使用缓存。
当
Expires
和Cache-Control
同时存在时,Cache-Control
优先级高于Expires
。
Expires:这是一个由服务器端所决定的绝对时间,表示资源的过期时间,以秒为单位。
Expires: Thu, 10 Nov 2017 08:45:11 GMT
- 由于是绝对时间,考虑到时差/误差或者客户端自行修改等因素,都会造成客户端与服务器端时间不一致,从而导致缓存失效。
- 字段表示过于复杂 👾,很容易因为非法属性值从而设置失效。
Cache-Control:相对时间
Cache-control: max-age=2592000 no-cache
- 到期:
max-age
代表最大有效时间,是一个相对时间,单位是秒。 - 可缓存性
public
:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)private
:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。no-cache
:不进行强制缓存的过期判断,而是直接使用协商缓存交由服务器判断。no-store
: 真正意义上的“不要缓存”。所有内容都不走缓存,包括强制和对比。
- 重新验证:
must-revalidate
一旦资源过期,在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。
协商缓存
当浏览器在第一次请求资源时,服务器不仅会在响应头中包含强制缓存的策略,还会标明与协商缓存有关的信息,例如:
Last-Modified
(对应请求头中的If-Modified-Since
)ETag
(对应请求头中的If-None-Match
)
他们的执行流程大概如下(以ETag
为例):
当首次请求资源时,服务器会在响应头中加入 ETag
字段,浏览器会将这个字段记录下来。再次请求资源时,浏览器会在请求头中加入 If-None-Match
字段,值为上次记录的 ETag
字段的值。
服务器会将这两个值进行对比,如果相等,则表示未修改,响应 304 Not Modified ;反之,则表示修改了,响应 200 OK
,并返回最新的数据与缓存标识。
而这两组字段的区别在于:
Last-Modified
是一个 GMT 格式的时间字符串,以秒为单位,表示资源的最后修改时间。如果资源在 1s 内被修改 🚀,那么这个字段就会失效。同时,如果文件是通过服务器动态生成🌊 的,那么该方法的更新时间永远是生成的时间,尽管文件可能并没有改变,但仍然会被认为改变了。ETag
是一个由服务器通过某些算法生成的字符串,只要资源有变化,这个值就会发生变化,因此可以更加精确🎉 的判断资源是否有变化。
跨域资源共享 🔥
跨域问题的产生源自浏览器的同源策略 🍨,同源策略是浏览器的一种安全策略,能够帮助隔离恶意文档,减少可能被攻击的媒介,主要用于防止一些常见的网络攻击(例如 CSRF 攻击)
同源策略指的是:如果两个 URL 的协议,端口(如果有指定的话)和域名都相同的话,则这两个 URL 是同源。只有同源的文档才能够自由地进行交互,否则就会受到限制。
解决跨域问题的方案
CORS 跨域资源共享(最为正统的解决方案 🏆): 是现代浏览器普遍支持的一种机制,允许服务器明确指示哪些其他源(domain、协议、端口)有权访问它的资源。服务器可以在响应头中添加特定的字段,例如
Access-Control-Allow-Origin / Access-Control-Allow-Methods
等等,来告诉浏览器这个请求是可以被允许的。代理:需要注意的是,跨域问题是浏览器的限制,所以在 Node 环境中并不存在跨域问题,我们可以通过搭建一个 NodeJS 代理服务器来实现对于跨域请求的处理。具体而言,如果你使用 Webpack 或者 Vite 你可以通过配置相应的插件来解决跨域问题,如果你使用 Node.js 的话,你可以使用
http-proxy-middleware
这个库来解决跨域问题。需要注意的是,这个办法只适用于开发环境 🚧。Nginx 反向代理💡:客户端向 Nginx 服务器发送请求,Nginx 服务器再将请求转发到真正的目标服务器,然后将目标服务器的响应返回给客户端。在这个过程中,我们需要配置 Nginx 服务器的反向代理规则,来对目标服务器的相应做进一步处理,例如修改响应头使其支持跨域请求。
简单请求与复杂请求
在 CORS 策略中请求可以分类简单请求与复杂请求。
- 简单请求通常是指那些可以直接由 HTML 发起的请求,例如使用 GET 或 POST 方法的请求。这类请求的特点是它们不会改变服务端的状态,并且只使用了一些安全的头信息集合。对于简单请求,浏览器会在请求头中添加一个 Origin 字段来表示请求的源。服务器可以根据这个字段来判断是否允许该请求。
- 复杂请求🎨 通常指的是那些需要通过 JavaScript 脚本发起,并且可能会改变服务端状态或使用了不属于简单请求头集合的请求,例如修改请求头中的
Content-Type
,或者使用 PUT、DELETE 等 HTTP 方法。
对于复杂请求,浏览器会首先发送一个预检(preflight)请求,这通常是一个 OPTIONS 方法的请求。预检请求中会包含 Access-Control-Request-Headers / Methods
等字段,用以表示实际请求中将会使用的 HTTP 方法和头信息等。服务器会根据这些信息来决定是否允许实际的请求。如果服务器允许,它会在响应头中添加 Access-Control-Allow-Headers
和其他相关字段。浏览器将根据这些响应头来决定是否继续发送实际的请求。如果不允许,浏览器将返回一个错误 ⚡。
输入 URL 到页面展示的过程 🎨
用户在地址栏中输入一个 URL 之后,将会发生以下几个过程:
DNS 解析 -> TCP 握手 -> TLS 握手 -> 请求与响应 -> 解析 -> 渲染
1. 导航
在网络的传输过程中,起点与终点是使用 IP 地址来标识的,而 IP 地址是比较难记忆的,所以才有了域名的出现。用户输入的 URL 通常是一个域名而非 IP,那么要做的第一件事情就是将域名解析成 IP 地址。这个过程被称之为 DNS 解析📚,其具体的过程是这样的:浏览器首先查看自己的缓存中是否有这个域名对应的 IP 地址,如果有的话则直接使用,如果没有则去查看本地 host 文件,如果还是没有的话则去请求 DNS 服务器,DNS 服务器会返回一个 IP 地址,然后浏览器就可以使用这个 IP 地址来发送请求了。
2. TCP 握手
获得了服务器的 IP 地址之后,浏览器就会通过 TCP 三次握手与服务器建立连接。
- 【你好,可以听到我讲话吗】浏览器发送
SYN
与序列号seq=x
给服务器 - 【可以的,你可以听到我讲话吗】服务器收到了发送
ACK=x+1 / SYN / seq=y
给浏览器 - 【可以听到】浏览器再发送一个
ACK=y+1
包给服务器
为什么需要三次握手呢?🙌
三次握手都能够确保通信双方能够接收到对方数据并做出响应(很好的理解方式就是上面的对话。 如果只有两次握手,服务器端并不能知道浏览器是否收到了自己的数据)
3. TLS 握手 🔥
对于使用 https 协议的网站,浏览器还需要与服务器进行 TLS 握手。
TLS 1.2 的握手过程(2 RTT):
- 浏览器发送 ClientHello 并告知服务器自己支持的加密套件、TLS 版本与随机数 A
- 服务端发送 ServerHello 并告知浏览器自己选择的加密套件、TLS 版本与随机数 B 同时附带自己的 CA 证书
- 浏览器验证 CA 证书的有效性:证书是否由可信机构颁发、证书中的域名是否与请求的域名一致、证书是否过期。随后生成一个随机数作为预主密钥并使用服务端的公钥加密这个随机数,发送给服务端。
- 服务端使用自己的私钥解密浏览器发送的预主密钥,并结合随机数 A/B 生成会话密钥 🔒。
在随后的数据传输过程中,浏览器与服务器使用会话密钥进行对称加密。
TLS 1.3 的握手过程(1 RTT):
- 浏览器告知服务器基本的 TLS 信息并发送一个随机数 A
- 服务器告知浏览器基本的 TLS 信息与 CA 证书,并发送随机数 B
TLS 1.3 中不再使用预主密钥。相反,双方使用 Diffie-Hellman 密钥交换来协商共享秘密。这个共享秘密,将会结合随机数 A/B 来生成会话密钥。一个简单的密钥交换过程如下:
- 浏览器选定一个共享密钥 X, 并利用自己的私钥 a 生成随机数
A = aX
- 服务器也利用共享密钥 X 和自己的私钥 b 生成随机数
B=bX
此后浏览器利用自己的私钥 a 能够计算出会话密钥 aB = abX
, 服务器也能够计算会话密钥 bA = baX
。
4. 请求与响应
成功建立连接之后,浏览器将会发送一个初始的 HTTP GET 请求,获得一个 HTML 文件。浏览器收到响应之后会解析 HTML 文件,如果 HTML 文件中包含了其他资源的引用(例如图片、样式表、脚本等),那么浏览器会再次发送请求获取这些资源。
5. 解析
浏览器首先会处理 HTML 标记并构造 DOM 树,当解析器发现非阻塞资源,例如图片或者 CSS 文件,浏览器会请求这些资源并且继续解析,但是对于没有 async/defer
属性的 script
标签会暂时停止对 HTML 的解析,直到脚本被下载并执行完毕。
async
: 异步下载,下载完成后立即执行defer
: 异步下载,等到 HTML 解析完成后再执行
为了优化解析 DOM 的过程,通常会有预加载解析器📝 的参与。它在构建 DOM 树的同时运行,用来扫描 HTML 文档,寻找可能需要加载的资源(例如图片、样式表、脚本、字体文件等)。当预加载解析器发现了这些资源之后,它会指示浏览器开始加载这些资源,即使它们还没有被解析器处理到。这样可以减少页面的加载时间,因为一些资源的下载和 DOM 的解析可以并行进行。
接下来浏览器将会处理 CSS 并构建 CSSOM 树。等待与处理 CSS 的过程并不会阻塞 DOM 的解析,但是会阻塞 JavaScript 的执行。这是因为 JavaScript 可能会去获取 DOM 元素的 CSS 样式,为了拿到正确的样式信息,需要等待 CSSOM 树的构建完成,否则可能会拿到错误、过时的样式信息。
6. 渲染
浏览器在构建 DOM 树和 CSSOM 树之后,就会开始构建渲染树。渲染树是由 DOM 树与 CSSOM 树合并而成的,它只包含了需要显示的节点以及这些节点的样式信息。渲染树构建完成之后,浏览器就可以开始布局(layout)与绘制(paint)。
- 布局:确定每个节点在屏幕上的确切位置
- 绘制:将元素的每个可视部分绘制到屏幕上,包括文本、颜色、边框、阴影等 🌈。
断开连接
当数据传输完毕之后,浏览器与服务器会通过四次挥手来关闭连接。
- 【支付完毕 💰】浏览器发送
FIN
与seq=x
给服务器,表明自己没有数据要发送了。 - 【好的,稍等我给您打印一下小票 📋】服务器收到了并发送
ACK=x+1
给浏览器,表明自己知道了。此时,服务器通常还有数据要处理和发送,所以并不立即关闭连接。 - 【谢谢惠顾】服务器发送
FIN
与seq=y
给浏览器,此时服务器已经处理完了所有的数据。 - 【嗯嗯好】浏览器收到并发送
ACK=y+1
包给服务器。此时,浏览器并不会立即关闭连接,而是等待一段时间以确保服务器收到了自己的确认包。
为什么需要第四次挥手后还需要进行超时等待环节?
最后一次挥手发出的 ACK 报文可能会在传输过程中丢失,如果服务器没有收到这个 ACK 报文,它就会重新发送 FIN 报文,倘若此时浏览器直接关闭了连接而不进行超时等待,那么服务器就会认为浏览器没有收到自己的 FIN 报文,从而导致服务器一直处于等待关闭状态。因此,超时等待阶段有两个关键目的:
- 确保服务器端接收到了客户端发出的 ACK 报文,从而可以安全地关闭连接并释放相关资源。
- 防止服务器端先前发送的一些旧数据包在网络延迟后到达客户端,干扰新建立的连接。
为什么需要四次挥手?
如果缺少最后一次挥手,那么服务器并不知道浏览器是否接收到了自己的数据,可能会导致服务器的连接处于半关闭状态,从而导致资源的浪费。
Web 安全 🛡️
- CSRF:攻击者利用用户已经认证的会话,在用户不知情的情况下向目标网站发起恶意请求。
- XSS:攻击者将恶意脚本注入到网页中,以窃取信息或进行其他恶意操作。
- SQL 注入:攻击者通过向应用程序输入恶意 SQL 语句 💣,欺骗后端数据库执行非预期的命令或查询,从而获得敏感信息。
CSRF - 跨站请求伪造
在一个 CSRF 攻击场景中,假如你现在登录了 bank.com 这个网站,网站中记录了你的 cookie 信息,然后你又打开了一个标签页,在这个标签页中,你访问了一个恶意网站 evil.com 👾,这个网站中存在一些恶意脚本,脚本会偷偷地向 bank.com 发送伪造的请求,请求会自动携带上 bank.com 的 cookie 信息。这样一来,恶意网站就可以利用这些信息施展它的恶意行动,比如神不知鬼不觉地转移你的资金 💸。
防范措施:
- 使用 cookie 的
sameSite
字段,此时 cookie 只能在符合同源策略的情况下才会被发送。 - CSRF Token💡:客户端发送请求时需要携带由服务器动态生成的 Token,服务器会验证 Token 的有效性。
XSS - 跨站脚本攻击
XSS 攻击又分为:存储型、反射型、DOM 型。他们的区别在于恶意代码的注入过程不同,但最终目的都是为了将恶意代码注入到网页中,以窃取信息或进行其他恶意操作。
- 反射型:攻击者构造出特殊的 URL,包含恶意代码。用户点击链接时,请求发送到服务器,服务器会将请求中的脚本作为数据的一部分反射回给浏览器,浏览器随机解析执行了这段恶意脚本。
- DOM 型 🛠️:仅在客户端发生。攻击者构造特定的输入,引起网站的前端更新,在更新过程中通过闭合标签等修改 DOM 结构的形式注入恶意代码。
- 存储型:攻击者将恶意代码提交到网站的数据库中,当用户访问网站时,恶意代码从数据库中读取并执行。
防范措施:
- 前后端在处理用户输入的内容时,都需要保持谨慎,对其中的特殊字符进行过滤和转义。
- 使用安全的编程方式,譬如避免使用危险的
innerHTML
去设置 HTML 内容,而是使用textContent
。
SQL 注入
SQL 注入是一种代码注入攻击,攻击者通过向应用程序输入恶意 SQL 语句,欺骗后端数据库执行非预期的命令或查询,从而获得敏感信息。简单来讲,SQL 注入就是服务器在执行 SQL 语句时将用户的输入也作为了 SQL 语句的一部分,从而导致了一些意外的结果。
例如 🌰,对于根据用户 ID 查询用户信息的接口,其内部的 SQL 语句可能是这样的:
SELECT * FROM users WHERE id = ${id}
如果用户输入的 id
是 1 OR 1=1
,那么 SQL 语句就会变成:
SELECT * FROM users WHERE id = 1 OR 1=1
在这种情况下,由于 OR 1=1
永远成立,查询结果竟然是全体用户的信息大曝光 👥!
防范措施:
- 使用参数化查询,即使用预编译的 SQL 语句,将用户输入的内容作为参数传入,而不是直接拼接到 SQL 语句中。
- 对用户的输入进行严格的校验 🔍 和过滤,避免用户输入的内容包含特殊字符。
HTTP 史记
- http 1.0: 无连接,且在浏览器端存在队头阻塞。
- http 1.1: 支持持久连接,将队头阻塞转移到服务器端,减少了数据传输的延迟,与此同时还引入了 HTTP 头部的缓存机制。
- http 2.0: 利用二进制分帧的特性实现多路复用,并引入了头部压缩与服务器推送等技术。
http 1.0
- 无连接:默认情况下,每个 HTTP 请求都会创建一个新的 TCP 连接,并且在请求完成后关闭此连接,这导致了较高的延迟和较低的效率(每个 HTTP 请求都需要进行 TCP 握手与挥手),尤其是在网页包含多个资源时。
- 不支持管道化:同一时间浏览器只能处理一个请求,后续请求必须等待前面的请求响应结束才能发送,这意味着如果一个请求如果没有得到响应,那么后续的所有请求都会被阻塞,这就是常说的“队头阻塞”💥。
http 1.1
- 长连接:引入
Connection: keep-alive
机制,允许单个 TCP 连接处理多个 HTTP 请求,减少了频繁握手与挥手带来的开销。 - 支持管道化:客户端能够向服务器发送多个请求,服务器端可以同时处理这些请求,但是需要按照请求的顺序依次响应,以便客户端能够区分每次请求的响应内容。这意味着如果一个请求没有处理完毕,哪怕后面的请求已经准备就绪,也会被服务器所阻塞,事实上这只不过将队头阻塞转移到了服务器端。
- 缓存:在 HTTP 头部中新增
Cache-Control
、Expires
、Last-Modified
等字段,用于指定资源的缓存策略。
http 2.0
- 二进制分帧:http 2.0 采用二进制格式传输数据,而非文本格式,并且 http 2.0 还将请求和响应分割为多个帧,以便支持多路复用。
- 多路复用:由于二进制分帧的特性,多个请求和响应能够在同一个连接上交错进行,接收方只需要根据帧上的流标识符即可将其组装为正确且完整的消息。由于不再关心请求和响应的先后顺序,因此可以解决“队头阻塞”问题。
- 头部压缩📦:对一些重复的 HTTP 头部字段(例如 cookie 等)进行压缩,以减少传输的流量。实现原理类似于双方协商一个编码表,然后对 HTTP 头部进行编码,最后在传输过程中解码,例如使用 'A' 来代指 Last-Modified。
- 服务器推送:服务器可以主动向客户端推送数据,而无需客户端发送请求。例如在用户请求 HTML 文件时,服务器就可以将对应的 CSS 文件和 JS 文件发送给客户端,而不必等待浏览器解析到相应位置时再发送请求。
HTTP 3
HTTP 3 是基于 QUIC(Quick UDP Internet Connection)协议实现的,事实上 HTTP 3 的主要作用就是保持 HTTP 基本语义,很多 http 2 的功能都移交给 QUIC 了。那么 QUIC 协议具有哪些特点呢?
- 解决 TCP 层面的队头阻塞: TCP 队头阻塞其实与 ACK 有一定的关系,后续的数据包需要等待前面的数据包的 ACK,这就导致了“队头阻塞”。QUIC 采用的解决办法与 http 2 的思想类似,就是为数据分配一个标识符,根据标识符来区分不同的数据包,从而不必进行排队等待 。
- 整合 TCP 握手与 TLS 握手:在 http 2 之中传递数据需要经过 TCP 握手与 TLS 握手,至少需要 2 RTT 才能进行数据传输,而在 QUIC 中,这两个步骤可以合并为一个步骤,从而减少了 RTT 的数量 ⚡。
- Connection ID 实现切网不重握:通常情况下,发生网络切换(例如从 WIFI 切到 4G)时,浏览器需要进行重新握手连接。QUIC 引入 Connection ID,通过 Connection ID 将不同网络的请求与响应关联起来 🦄,从而实现切网不重握。
关于该部分的内容可以参考以下视频:
Web Storage🏦
sessionStorage
是临时性的本地存储,数据仅保存在一次会话(页面)中,关闭页面后数据会被释放。localStorage
是持久化的本地存储,存储在其中的数据不会过期,除非手动删除或者浏览器清除缓存。cookie
Cookie 🍪 不是专门设计用于数据存储的机制,主要作用在于维持状态,由于 http 协议本身是无状态的,每次请求都是独立的。为了跟踪用户的连续请求和会话,就需要借助额外的机制来维持状态。Cookie 主要用于维持用户的登录状态以及记录用户的偏好设置。其工作原理如下:
当服务器通过响应头中的 Set-Cookie
字段设置 Cookie 后,浏览器会将其保存在本地。随后,在发起下一次请求时,浏览器会自动将这些 Cookie 信息附带在请求头部。以下是一个简化的示例,注意若需设置多个 Cookie,则需在响应头中添加多个 Set-Cookie
字段:
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: user_name=xxx
Set-Cookie: status=xxx; Path=/; Domain=juejin.cn; Max-Age=2592000; HttpOnly
[页面内容]
Set-Cookie 字段中常见的参数一般有:
<key>=<value>
:定义 Cookie 的名称与值。- 生命周期 ⌛:
Expires
绝对时间、Max-Age
相对时间。默认为会话结束时失效。 - 安全策略:
Secure
只有在 https 请求中才会发送、HttpOnly
禁止 JavaScript 访问。 - 作用域:
Domain
指定哪些域名可以访问,默认为当前 host ,并且不包括子域名。若指定了该字段,那么子域名也可以访问,这常用于实现单点登录功能。Path
指定哪些 URL 可以访问。例如如果设置Path=/user
,则只有访问/user
下的 URL 才会携带这个 cookie。SameSite
通常用于防止 CSRF 攻击,设置为strict
时,只有同源请求才会携带 cookie。
需要注意的是 🧐,发送请求时并不会将所有 Cookie 都发送给服务器,而是要经过一系列的判断,例如是否过期,是否符合安全策略与作用域等等。
CDN 原理
在未使用 CDN 加速的情况下,用户的浏览器直接向源服务器请求加载所需资源。这种情况下,如果用户与源服务器之间的距离较远,或者网络条件不佳,可能会导致较长的加载时间 🐌。
启用 CDN 加速后,浏览器请求资源的流程发生变化:
- 请求分发✨:用户发起的资源请求首先到达 CDN 的边缘服务器。CDN 服务器会根据用户的地理位置与网络条件,通过全局负载均衡的手段,将用户请求分发到离用户最近的 CDN 节点上,这一步通常是利用 dns 解析的 CNAME 记录来实现。
- 资源缓存:当请求到达相应的 CDN 节点时:
- 如果该节点已经缓存了用户请求的资源,则直接从节点返回给用户,减少响应时间和网络传输距离。
- 如果 CDN 节点上没有该资源,CDN 节点会向源服务器请求资源,缓存并返回给用户。后续请求时就可以直接使用。
需要注意的是使用 CDN 并不总是能加速资源加载🚨。若 CDN 节点上的资源已经过期,节点需要重新向源服务器请求资源,这时 CDN 相较于直接访问源服务器的加速效果就会减弱(因为多走了一个 CDN 节点)。
CDN 主要解决了跨地域访问带来的延迟问题,以及减轻源服务器的负载压力。
计算机网络
前文内容快速跳转
TCP 与 UDP 的区别
TCP:面向连接的、可靠的、基于字节流的传输层协议。TCP 通过三次握手建立连接,并借助序列号、ACK、超时重传等机制保证数据的可靠传输,并且提供流量控制和拥塞控制。
UDP:无连接的、不可靠的、基于数据包的传输层协议。UDP 不保证数据的可靠传输,但是传输速度更快,并且支持广播和多播。
- TCP 适用于要求可靠传输的场景,例如 HTTP、HTTPS、SMTP 等。
- UDP 适用于实时性要求较高的场景,例如音视频、DNS 等。
补充内容:可靠传输的定义
- 数据不丢失:接收方能够接收到发送方发送的所有数据。
- 数据不重复:接收方不会接收到重复的数据。
- 数据有序:接收方接收到的数据与发送方发送的数据顺序一致。
- 数据完整性:数据在传输过程中没有被篡改或损坏。
常用的 HTTP 请求方式
- GET:用来请求获取某个网页的信息,并返回网页内容。
- POST:用来向指定的资源提交数据进行处理,比如提交表单或者上传文件。数据包含在请求体中。
- PUT:用来将客户端提供的数据替换指定资源的内容。
- DELETE:用来请求服务器删除指定的资源。
- OPTIONS:用来查看服务器支持哪些 HTTP 请求方法,通常用于跨域请求的预检操作。
GET 与 POST 的区别
GET | POST | |
---|---|---|
用途 | 获取数据 | 提交数据,通常用于创建或者更新服务器上的资源 |
参数传递方式 | URL 查询字符串 | 请求体 |
安全性 | 参数包含在 URL 中,安全性较低 😱 | 参数在请求体中,相对更安全 |
数据长度限制 | 有长度限制(通常不超过 2048 字符) | 无明确长度限制 |
幂等性 | 幂等,重复请求不会改变服务器状态 | 非幂等,重复请求可能会改变服务器状态 |
缓存 | 浏览器和缓存服务器会缓存 | 默认不缓存 |
常见的 HTTP 状态码
2xx 成功
- 200 OK:请求成功,服务器返回所请求的数据。
- 201 Created:请求成功并且服务器创建了新的资源。
- 204 No Content:请求成功但没有内容返回。
3xx 重定向
- 301 Moved Permanently:请求的资源已被永久移动到新的 URL。
- 302 Found:请求的资源临时从不同的 URL 响应。
- 304 Not Modified:资源未修改,客户端可以使用缓存的版本。
4xx 客户端错误 🚧
- 400 Bad Request:服务器无法理解请求的格式。
- 401 Unauthorized:请求要求用户认证。
- 403 Forbidden:服务器理解请求但拒绝执行。
- 404 Not Found:请求的资源未找到。
5xx 服务器错误
- 500 Internal Server Error:服务器遇到错误,无法完成请求。
- 502 Bad Gateway:服务器作为网关或代理,从上游服务器收到无效响应。
- 503 Service Unavailable:服务器当前无法处理请求,通常是由于过载或维护。ChatGPT 过于火爆时就会报这个错误码 👾.
- 504 Gateway Timeout:服务器作为网关或代理,未能及时从上游服务器接收到响应。
对 WebSocket 与 SSE 的理解
- WebSocket:是一种在单个 TCP 连接上进行全双工通信的协议,它允许客户端和服务器之间进行实时通信 🔗 。WebSocket 通过 HTTP 协议进行握手,然后在握手成功后升级为 WebSocket 协议。WebSocket 适用于实时性要求较高的场景,例如在线聊天、实时游戏等。
- SSE(Server Send Event):是一种基于 HTTP 的单向通信协议,允许服务器向客户端推送数据。SSE 适用于服务器向客户端推送实时数据的场景,例如
ChatGPT
的流式 🌊 对话功能。