4. HTTP 缓存头详解:Cache-Control、Expires、ETag、Last-Modified 在 OpenAPI 中的映射
HTTP 缓存是提升 API 响应速度、降低服务器负载的核心手段。在 OpenAPI 契约中,正确映射缓存头能够使客户端与网关层获得明确的缓存策略,避免因缓存失效导致的接口调用错误或数据不一致。本章将逐一拆解 Cache-Control、Expires、ETag、Last-Modified 四个关键头,并给出它们在 OpenAPI 规范中的标准映射方式。
4.1 缓存头分类与作用域
HTTP 缓存头按作用机制可分为两类:强缓存(无需请求服务器)与 协商缓存(需与服务器验证)。下表列出四类头的归属与核心字段:
| 缓存类型 | 响应头 | 核心作用 | 优先级 |
|---|---|---|---|
| 强缓存 | Cache-Control | 指定缓存策略(max-age、public/private、no-cache 等) | 最高 |
| 强缓存 | Expires | 指定绝对过期时间(HTTP/1.0 兼容) | 低于 Cache-Control |
| 协商缓存 | ETag | 资源唯一标识(哈希/版本号),用于条件请求 | 与 Last-Modified 配合 |
| 协商缓存 | Last-Modified | 资源最后修改时间,用于 If-Modified-Since 验证 | 低于 ETag |
4.2 Cache-Control 指令详解与 OpenAPI 映射
Cache-Control 是 HTTP/1.1 定义的缓存控制头,支持多个指令组合。在 OpenAPI 中,通过 headers 字段定义响应头,并利用 schema 约束指令值。
4.2.1 常用指令速查表
| 指令 | 含义 | 示例值 | 适用场景 |
|---|---|---|---|
| max-age=<seconds> | 资源在缓存中的最大有效时间(秒) | max-age=3600 | 静态资源、不频繁更新的列表 |
| public | 允许任何缓存(包括 CDN)存储 | public, max-age=86400 | 公开的静态数据 |
| private | 仅允许客户端(浏览器)缓存 | private, max-age=600 | 用户个性化数据 |
| no-cache | 强制每次请求都验证资源是否变化 | no-cache | 动态数据、需实时校验的接口 |
| no-store | 禁止任何缓存 | no-store | 敏感信息、支付令牌 |
| must-revalidate | 过期后必须向源服务器验证 | must-revalidate | 强一致性要求的数据 |
4.2.2 OpenAPI 映射示例
以下 YAML 片段展示如何在 OpenAPI 3.0 中为 /api/products 响应定义 Cache-Control 头:
paths:
/api/products:
get:
summary: 获取产品列表(可缓存)
responses:
'200':
description: 成功返回产品列表
headers:
Cache-Control:
description: 缓存控制策略
schema:
type: string
example: "public, max-age=3600, must-revalidate"
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Product'
type: string,并通过 example 或 enum 约束合法指令组合,避免客户端解析歧义。
4.3 Expires 头与兼容性映射
Expires 是 HTTP/1.0 的强缓存头,值为绝对时间(RFC 1123 格式)。当 Cache-Control 存在时,Expires 被忽略。在 OpenAPI 中,Expires 通常作为 后备方案 定义。
4.3.1 OpenAPI 映射示例
headers:
Expires:
description: 资源过期时间(HTTP/1.0 兼容)
schema:
type: string
format: date-time-rfc1123
example: "Wed, 21 Oct 2025 07:28:00 GMT"
format 在 OpenAPI 3.0 中没有标准枚举值,建议使用 date-time 并附加文档说明。更推荐的做法是仅使用 Cache-Control,避免时间同步问题。
4.4 ETag 与 Last-Modified:协商缓存双引擎
协商缓存需要客户端在请求中携带条件头(If-None-Match / If-Modified-Since),服务器返回 304 Not Modified 或完整响应。在 OpenAPI 中,需同时定义响应头与请求头。
4.4.1 ETag 映射
ETag 是资源内容的强校验器,通常为哈希值。OpenAPI 定义如下:
# 响应头定义
headers:
ETag:
description: 资源唯一标识(强校验)
schema:
type: string
example: "686897696a7c876b7e"
# 请求头定义(客户端条件请求)
parameters:
- name: If-None-Match
in: header
description: 客户端缓存的 ETag 值
schema:
type: string
required: false
4.4.2 Last-Modified 映射
Last-Modified 是弱校验器,精度为秒级。OpenAPI 定义:
# 响应头定义
headers:
Last-Modified:
description: 资源最后修改时间
schema:
type: string
format: date-time
example: "2025-03-15T14:30:00Z"
# 请求头定义
parameters:
- name: If-Modified-Since
in: header
description: 客户端缓存的最后修改时间
schema:
type: string
format: date-time
required: false
4.5 缓存头在 OpenAPI 中的完整映射流程
下图展示了一个典型 GET 请求的缓存协商流程,以及 OpenAPI 中各头的对应位置:
4.6 缓存头组合策略与 OpenAPI 契约设计
根据 API 的数据特性,推荐以下四种缓存头组合策略,并在 OpenAPI 中通过 description 或 x-cache-policy 扩展字段标注:
| 策略 | Cache-Control | ETag | Last-Modified | 适用场景 |
|---|---|---|---|---|
| 静态资源 | public, max-age=86400, immutable | 可选 | 可选 | 图片、CSS、版本化 JS |
| 列表数据 | public, max-age=300, must-revalidate | 强 ETag | 推荐 | 产品列表、文章目录 |
| 用户数据 | private, max-age=60 | 强 ETag | 推荐 | 用户信息、订单状态 |
| 敏感数据 | no-store | 不适用 | 不适用 | 令牌、密码、支付信息 |
x-cache-policy 自定义字段在操作级别标注缓存策略,便于工具链自动生成缓存配置。例如:x-cache-policy: "static-asset" 或 x-cache-policy: "user-private"。
4.7 常见避坑指南
- Expires 与 Cache-Control 冲突:当两者同时存在时,Cache-Control 的 max-age 覆盖 Expires。OpenAPI 中建议只定义 Cache-Control。
- ETag 弱校验器:ETag 值前加
W/前缀表示弱校验(如W/"686897"),OpenAPI 中需在文档中说明校验强度。 - Vary 头缺失:如果响应内容因 Accept-Encoding 或 Accept-Language 变化,必须在 OpenAPI 中定义 Vary 头,否则缓存层可能返回错误版本。
- 304 响应体为空:OpenAPI 中 304 响应应定义
content为null或省略,避免客户端解析错误。
# 304 响应在 OpenAPI 中的正确定义
responses:
'304':
description: Not Modified - 使用缓存
headers:
ETag:
schema:
type: string
# 不包含 content 字段