HTTP 核心:请求响应、缓存、CORS 与 HTTP/2/3

前端必须掌握的 HTTP 知识体系——方法、状态码、Headers、缓存策略、跨域、协议演进

1. 请求/响应模型

客户端(浏览器)                      服务端
    │                                  │
    │  ──── 请求(Request)────→       │
    │                                  │
    │  ←──── 响应(Response)────       │
    │                                  │

无状态协议: 每次请求独立,服务端不记住上一次请求。需要状态就用 Cookie / Token / Session。


2. HTTP 方法

GET     获取资源          幂等(多次请求结果一样)
POST    创建资源          非幂等
PUT     替换资源          幂等
PATCH   部分更新          非幂等
DELETE  删除资源          幂等
HEAD    只要响应头        幂等
OPTIONS 预检请求          CORS 用

幂等性: 同一个请求发 1 次和发 10 次,服务端状态一致。GET 天然幂等,POST 不是。


3. 状态码

2xx 成功
  200 OK                    请求成功
  201 Created               资源创建成功(POST 之后)
  204 No Content            删除成功,无返回体

3xx 重定向
  301 Moved Permanently     永久重定向(SEO 友好)
  302 Found                 临时重定向
  304 Not Modified          缓存命中,不需要重新请求

4xx 客户端错误
  400 Bad Request           请求格式错误
  401 Unauthorized          未认证(没有 Token)
  403 Forbidden             无权限
  404 Not Found             资源不存在
  429 Too Many Requests     请求频率超限

5xx 服务端错误
  500 Internal Server Error 服务端崩了
  502 Bad Gateway           网关错误
  503 Service Unavailable   服务不可用

4. Headers

请求头:
  Accept: application/json          期望的响应格式
  Content-Type: application/json    发送的数据格式
  Authorization: Bearer <token>     认证令牌
  Cache-Control: no-cache           缓存策略
  If-None-Match: "abc123"           协商缓存(ETag)
  If-Modified-Since: Wed, 21 Oct... 协商缓存(Last-Modified)
  Origin: https://example.com       来源(CORS 用)

响应头:
  Content-Type: application/json    响应数据格式
  Cache-Control: max-age=3600       缓存策略
  ETag: "abc123"                    资源版本标识
  Last-Modified: Wed, 21 Oct...     最后修改时间
  Access-Control-Allow-Origin: *    CORS 允许的来源
  Set-Cookie: token=xxx             设置 Cookie

5. 缓存

强缓存: 浏览器直接用本地缓存,不发请求。

Cache-Control: max-age=3600     相对时间(推荐)
Expires: Wed, 21 Oct 2025...    绝对时间(已废弃)

协商缓存: 带条件请求服务端,没变就返回 304(不传 body)。

// 第一次请求
ETag: "v2"                      服务端返回资源标识
Last-Modified: Wed, 21 Oct...   服务端返回修改时间

// 第二次请求(带上条件)
If-None-Match: "v2"
If-Modified-Since: Wed, 21 Oct...

// 服务端比较
// 没变 → 304 Not Modified(不用重新传 body)
// 变了 → 200 + 新资源

优先级: Cache-Control: no-cache \> ETag \> Last-Modified

no-cache vs no-store:**

no-cache  每次都要跟服务端验证(协商缓存)
no-store  完全不缓存

6. CORS(跨域)

同源策略: 协议 \+ 域名 \+ 端口都相同才算同源。不同源的请求被浏览器拦截。

简单请求: GET/POST/HEAD \+ 简单 Header → 直接发,服务端返回 Access-Control-Allow-Origin

预检请求(Preflight):

浏览器发现是复杂请求(PUT/DELETE/自定义 Header/JSON body)

先发 OPTIONS 请求(预检)

服务端返回:
  Access-Control-Allow-Origin: https://example.com
  Access-Control-Allow-Methods: GET, POST, PUT, DELETE
  Access-Control-Allow-Headers: Content-Type, Authorization
  Access-Control-Max-Age: 86400  (预检结果缓存 24 小时)

浏览器确认允许 → 发真正请求

7. HTTP/2 vs HTTP/3

HTTP/1.1:
  - 一个连接一个请求(队头阻塞)
  - Keep-Alive 复用连接,但还是串行

HTTP/2:
  - 多路复用:一个连接上并行多个请求(二进制分帧)
  - 头部压缩:HPACK 算法
  - 服务端推送:主动推资源
  - 缺点:TCP 层的队头阻塞还在

HTTP/3:
  - QUIC 协议(基于 UDP)
  - 彻底解决队头阻塞
  - 0-RTT 连接建立
  - 连接迁移(Wi-Fi → 4G 不断线)

8. fetch \+ AbortController

const controller = new AbortController()

fetch('/api/data', { signal: controller.signal })
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') console.log('请求取消了')
  })

// 组件卸载时取消请求
useEffect(() => {
  const controller = new AbortController()
  fetchData({ signal: controller.signal })
  return () => controller.abort()
}, [])

跟 TanStack Query 的关系: TanStack Query 内部就是用 AbortController 管理请求取消的,组件卸载时自动 abort。


核心一句话

HTTP 是无状态的请求/响应协议。缓存靠 Cache-Control \+ ETag,跨域靠 CORS 预检,实时通信靠 WebSocket/SSE,请求取消靠 AbortController。