3. OpenAPI 中的缓存声明:定义缓存策略的字段与方法

在 OpenAPI 3.0 规范中,没有为缓存策略提供专用的顶层字段或独立的“缓存声明”对象。缓存行为是通过组合使用 Cache-Control 响应头字段、ETag、Last-Modified 等标准 HTTP 头,以及 OpenAPI 的 response 对象中的 headers 字段来声明的。这种设计遵循了“规范即契约”的原则,将缓存策略的声明权交给 HTTP 语义,而非引入专有结构。

3.1 核心声明字段:response.headers

在 OpenAPI 3.0 的 Response Object 中,headers 字段用于定义响应头。缓存策略的声明主要集中在此处。以下表格列出了最常用的缓存相关响应头及其在 OpenAPI 中的声明方式。

HTTP 响应头 OpenAPI 声明位置 说明 示例值
Cache-Control response.headers.Cache-Control 定义缓存指令(max-age、public、private、no-cache 等) public, max-age=3600
ETag response.headers.ETag 实体标签,用于条件请求(强验证器) "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified response.headers.Last-Modified 资源最后修改时间(弱验证器) Wed, 21 Oct 2023 07:28:00 GMT
Expires response.headers.Expires 指定响应过期的绝对时间(HTTP/1.0 兼容) Thu, 01 Dec 2023 16:00:00 GMT
Vary response.headers.Vary 告知缓存代理根据哪些请求头区分缓存版本 Accept-Encoding, Authorization
审计要点: 在契约审计中,必须检查 Cache-Control 是否被显式声明。缺失该字段意味着缓存行为完全由客户端或中间件自行决定,可能导致缓存污染或数据不一致。

3.2 声明方法:在 OpenAPI 规范中编写缓存头

在 OpenAPI 3.0 中声明缓存策略,本质上是为每个响应对象定义其 headers 映射。以下是一个完整的 YAML 示例,展示了如何为 GET /users/{userId} 端点声明缓存策略。

paths:
  /users/{userId}:
    get:
      operationId: getUserById
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 成功返回用户信息
          headers:
            Cache-Control:
              description: 缓存控制指令
              schema:
                type: string
                example: private, max-age=300
            ETag:
              description: 实体标签,用于条件请求
              schema:
                type: string
                example: "abc123"
            Last-Modified:
              description: 资源最后修改时间
              schema:
                type: string
                format: date-time
                example: "2023-10-21T07:28:00Z"
            Vary:
              description: 缓存键差异化字段
              schema:
                type: string
                example: Authorization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
最佳实践: 对于需要强一致性的资源(如用户余额),应声明 Cache-Control: no-cache, no-store, must-revalidate。对于公开的静态资源(如产品列表),应声明 Cache-Control: public, max-age=86400 并配合 ETag 使用。

3.3 缓存策略的两种声明模式

根据资源特性,缓存策略的声明可分为两种模式:基于时间的缓存 和 基于验证器的缓存。下表对比了这两种模式在 OpenAPI 中的声明差异。

模式 核心字段 适用场景 审计关注点
基于时间 Cache-Control: max-age、Expires 静态资源、不频繁变化的数据 max-age 值是否合理;是否同时声明了 Expires 作为降级
基于验证器 ETag、Last-Modified 动态资源、需要条件请求的场景 ETag 是否强验证器;Last-Modified 精度是否足够

3.4 缓存声明与条件请求的契约关系

在 OpenAPI 规范中,声明了 ETag 或 Last-Modified 响应头,意味着客户端可以发送 If-None-Match 或 If-Modified-Since 请求头来发起条件请求。这种契约关系必须在 API 文档中明确体现。

以下 SVG 图展示了缓存声明与条件请求之间的完整契约流程。

缓存声明与条件请求契约流程 OpenAPI 规范层 声明响应头:Cache-Control: public, max-age=3600 声明验证器:ETag: "abc123" 服务端实现层 根据规范生成响应头,计算 ETag 值 处理 If-None-Match 条件请求,返回 304 或 200 客户端契约层 根据 Cache-Control 决定是否缓存响应 根据 ETag 构造 If-None-Match 请求头 契约审计:检查规范、实现、客户端三者是否对齐

3.5 缓存声明中的常见审计缺陷

在审计 OpenAPI 缓存声明时,以下缺陷最为常见,需要重点检查。

  • 缺失 Cache-Control 字段:超过 60% 的 OpenAPI 规范未声明 Cache-Control,导致缓存行为不可控。
  • ETag 与 Last-Modified 不一致:同时声明两者时,ETag 的更新频率应与 Last-Modified 匹配,否则客户端条件请求可能失效。
  • Vary 字段声明不完整:当响应内容因 Accept-Encoding 或 Authorization 而异时,未声明 Vary 会导致缓存代理返回错误的缓存版本。
  • max-age 值过大或过小:未根据资源实际变化频率设置合理的 max-age,导致数据过期或缓存命中率低。
安全警告: 对于包含敏感信息的 API 响应(如个人身份信息、金融数据),Cache-Control 必须设置为 no-store,禁止任何形式的缓存。审计时若发现此类响应缺少 no-store 指令,应标记为高风险缺陷。

3.6 缓存声明与 API 版本的关系

在 OpenAPI 3.0 中,缓存声明应随 API 版本变化而调整。以下 SVG 图展示了不同版本下缓存策略的演进关系。

API 版本演进中的缓存策略调整 v1.0 Cache-Control: public max-age=86400 无 ETag v2.0 Cache-Control: public max-age=3600 ETag: 强验证器 v3.0 Cache-Control: private max-age=300 ETag + Vary 版本演进说明 v1.0:粗粒度缓存,适合静态资源,但缺乏验证器 v2.0:引入 ETag,支持条件请求,缓存粒度细化 v3.0:改为 private 缓存,加入 Vary 头,适配多租户场景 审计要求:每个版本必须重新评估缓存策略的适用性

3.7 审计基线:缓存声明检查清单

在契约审计中,针对 OpenAPI 缓存声明,应逐项检查以下基线条目。

  1. 必选检查:每个 200 响应是否都声明了 Cache-Control 头?
  2. 验证器检查:对于动态资源,是否同时声明了 ETag 或 Last-Modified?
  3. 安全检查:包含敏感数据的响应是否设置了 Cache-Control: no-store?
  4. 一致性检查:ETag 的 schema 类型是否为 string,且示例值格式正确?
  5. Vary 检查:当响应内容因请求头而异时,是否声明了 Vary 字段?
  6. 过期时间检查:max-age 值是否与资源实际变化频率匹配?
审计工具建议: 使用 Spectral 或 Vacuum 等 OpenAPI 规则引擎,可以编写自定义规则自动检查 Cache-Control 字段的存在性和值范围。例如,规则 no-store-for-sensitive-endpoints 可确保所有包含 password 或 token 的响应路径都设置了 no-store。