HTTP协议与请求方法归纳总结

聊SDK缓存架构,绕不开HTTP协议。

你想想看,SDK的API调用本质就是HTTP请求。我做了多年前后端,见过不少团队把缓存设计得很复杂,结果问题往往出在最基础的HTTP方法理解上。今天我们就掰开揉碎,把HTTP协议和请求方法彻底讲清楚。

1. HTTP协议核心:无状态但有语义

HTTP本身是无状态的。每次请求都是独立的,服务器不记住你上一次干了什么。但RESTful API设计赋予了它语义——通过请求方法、状态码、头部信息表达意图。

举个例子:你调用一个获取用户信息的接口,用GET还是POST?很多人觉得无所谓,但我说,这能决定你的缓存策略是否有效。我在项目中遇到过因为用POST做查询,导致缓存完全失效的惨案。

核心原则: 请求方法决定了请求的幂等性和安全性,进而影响缓存行为。

2. 请求方法详解

先看一张表,把主流方法的特性理清楚。

方法 语义 幂等 安全 可缓存 典型场景
GET 获取资源 是 是 是 查询用户、列表等
POST 创建资源 否 否 仅支持新鲜度校验 新增订单、注册
PUT 全量更新 是 否 否 替换用户信息
PATCH 部分更新 否 否 否 修改用户昵称
DELETE 删除资源 是 否 否 删除文章
HEAD 获取响应头 是 是 是 预检、缓存校验
OPTIONS 查询支持的方法 是 是 否 CORS预检

这里有两个关键概念:幂等和安全。

  • 安全方法:不会修改服务器状态,比如GET和HEAD。你调用一万次,数据不变。
  • 幂等方法:无论调用多少次,结果和调用一次相同。PUT、DELETE是幂等的——但注意POST不是。

为什么我要强调这个?因为缓存系统通常只缓存安全且幂等的方法。但你看看PATCH,它既不是安全的也不是幂等的,浏览器和代理默认不会缓存它。你在SDK中如果误用PATCH做更新后的查询,缓存层直接跳过。

避坑指南: 我曾经在一个项目中,同事用POST做复杂查询,还加了Cache-Control头。结果因为POST默认不可缓存,缓存一直不命中,服务端压力巨大。后来改成GET + body(虽然不规范),配合ETag,性能才上来。

3. 缓存相关的HTTP头部

SDK缓存架构的核心,就是利用HTTP头部控制缓存行为。说几个必知必会的。

头部 说明 示例
Cache-Control 指定缓存策略,如max-age=3600 Cache-Control: public, max-age=3600
ETag 资源版本标识符,用于条件请求 ETag: "abc123"
Last-Modified 资源最后修改时间 Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
Expires 过期时间(HTTP/1.0,已被Cache-Control取代) Expires: Thu, 01 Dec 2023 16:00:00 GMT
Vary 根据请求头区分缓存版本 Vary: Accept-Encoding

我个人习惯在SDK的HTTP客户端里设置默认的Cache-Control策略。对于GET请求,如果服务端没有返回缓存头,客户端可以按业务规则补一个max-age=60作为兜底。但要注意,这需要和服务端约定好。

4. 请求方法与缓存决策流程图

下面这张图,把缓存决策逻辑串起来。你对照着看,SDK里的缓存中间件干的就是这个事。

HTTP请求方法缓存决策流程图 HTTP请求方法 - 缓存决策流程 收到API请求 请求方法是什么? GET / HEAD / OPTIONS ? 否(POST/PUT/DELETE等) 跳过缓存 直接发送请求 是(GET/HEAD/OPTIONS) 检查本地缓存 是否有有效条目? 命中 返回缓存数据 (附带缓存头) 未命中 向服务端发起请求 响应可缓存? (Cache-Control / ETag) 是 写入本地缓存 设置过期策略 否 不缓存响应 直接返回 返回结果给调用方 缓存决策要点 • 只有安全方法才缓存 • 未命中时用条件请求 • 响应头决定缓存策略 • 注意Vary头影响
实战建议: 在上图流程中,SDK的缓存层应该对GET请求先检查本地缓存的有效性。如果缓存过期但ETag还在,可以用条件请求(If-None-Match)减轻服务端压力。我自己的SDK里,这部分逻辑通常写成一个拦截器,对所有GET请求生效。

5. 实践中的常见陷阱

最后聊几个坑。我说过,SDK设计的好坏,往往体现在对异常和边界的处理上。

  • POST请求被缓存中间件拦截: 有些团队在SDK中引入统一缓存层,对所有请求都查询缓存。结果POST创建请求被错误地返回了旧数据。解决办法:在缓存中间件里按方法白名单过滤。
  • 忽略Vary头: 如果服务端返回了Vary: Authorization,而你的SDK缓存没考虑这个,不同用户的请求可能共享同一个缓存条目,导致数据泄露。这是个安全漏洞。
  • ETag配对失误: 服务端换了资源但ETag没变,客户端缓存一直命中旧数据。我建议在SDK缓存中加入stale-while-revalidate策略:先返回旧数据,同时在后台异步刷新。
  • 缓存写入时机不对: 有些SDK在收到响应后就写入缓存,但没检查响应状态码。如果服务器返回了500,你也缓存进去,后续所有请求都拿错误结果。必须只缓存2xx和3xx响应。
严重警告: 不要在SDK中缓存包含敏感信息的响应到持久化存储(如文件、SQLite)。除非你做了加密且安全策略明确。否则一旦用户手机被root,数据就裸奔了。