用户登录

技术文章

外部链接将在新标签页打开。

接口响应设计规范:内容协商、条件请求与二进制边界

一句话回答

面向开发者的响应设计说明:先确定端点允许哪些表示、由谁选择、选不中时如何失败,再处理 JSON、表单与二进制三种响应的边界,最后用 Vary、ETag 与条件请求保证缓存不误用。文中示例为未执行的占位示例,不涉及 API.PLUS 任何端点的实际响应。

核心要点 TL;DR
  • 响应规范的第一层是表示协商,而不是字段命名:Accept 与 Content-Type 必须成对声明,选不中时返回 40
  • 内容协商的端点必须带 Vary: Accept,否则共享缓存会把一种表示发给只接受另一种表示的客户端。
  • 二进制响应优先独立端点并配 ETag 与条件请求;JSON 内联 base64 只适合小对象,编码后体积约为原始的 4/

设计一套响应规范时,最先要确定的不是字段名,而是一个端点允许返回哪些「表示」(representation),以及由谁来决定用哪一种。客户端用 Accept 表达偏好,服务端用 Content-Type 声明实际返回的内容;只要这两者能对上,客户端才可能写出一份稳定的解析代码。JSON、表单与图片三类响应的差异,本质上都落在这一层。本文讨论的是通用的取舍与检查项,不描述 API.PLUS 任何具体端点的实际响应;其端点支持的表示、错误字段与缓存头需以官方文档和实际调用为准,本次材料未提供,待核实。

内容协商:把「选表示」写进契约#

常见做法是让同一个 URL 既能返回 JSON 元数据,也能返回图片本体。这种设计成立的前提是三条都写清楚:支持哪些媒体类型、偏好如何排序、选不中时怎么失败。

  • 能力集合要窄且明确。声明支持 application/json 与 image/webp、image/png,比声称「支持任意图片格式」更容易被客户端正确依赖。
  • 偏好排序交给 Accept 的 q 值。服务端按 q 值从高到低挑第一个自己支持的表示即可,不需要引入额外的查询参数。
  • 选不中时返回 406 Not Acceptable,并在错误体里列出可接受范围。静默退回到 JSON,会让只写图片解码逻辑的客户端在解析阶段报错,排查成本远高于直接收到 406。

与协商配套的是一个容易漏掉的响应头:Vary: Accept。它的作用是告诉缓存「同一 URL 的响应随请求头变化」。缺少它时,CDN 可能把一份 webp 图片缓存下来,再发给只声明接受 png 的客户端。协商类端点如果同时依赖鉴权头或语言头,也应一并写入 Vary。

三种响应的边界#

JSON:元数据为主,小二进制才内联

把图片放进 JSON 通常用 base64。按 base64 的编码规则,结果体积约为原始数据的 4/3,客户端还要多一次解码,超出这个代价的场景应改用独立二进制端点。JSON 更适合承载尺寸、格式、校验值、过期时间这类元数据。

二进制:独立端点 + 具体子类型

二进制响应应把 Content-Type 写到子类型级(如 image/webp),配合 Content-Length、Content-Disposition 与 ETag。大对象走独立端点,也便于单独设置缓存策略,不与元数据接口的缓存周期绑定。

表单:注意 boundary 与错误页

multipart/form-data 的 boundary 必须由服务端在 Content-Type 中给出,不能由客户端猜测或手写。另一个常见问题是错误响应:表单类端点有时会返回 HTML 错误页,而调用方是脚本。建议 API 端点无论成功失败都返回同一套结构化错误体。

条件请求与错误体#

图片这类大对象从缓存验证中收益最明显:响应带 ETag,客户端下次带 If-None-Match,命中即返回 304,不重复传输主体。需要注意 304 只对幂等的 GET 成立,且响应仍应带上 Vary 与相应缓存指令。

错误响应同样要遵守协商结果:一个声明只接受 JSON 的客户端,不应该在出错时收到 HTML。错误体里保留稳定的机器可读字段(如 code),把面向人的说明放在另一个字段,避免调用方解析自然语言。也不要用 200 包裹业务错误,这与状态码的语义冲突,会让中间层无法正确重试或缓存。

# 未执行的示例,主机与令牌为占位
GET /v1/preview/42 HTTP/1.1
Host: api.example.com
Accept: image/webp, image/png;q=0.8, application/json;q=0.5
If-None-Match: "preview-42-v3"
Authorization: Bearer YOUR_KEY
# 未执行的示例,错误体仅示意字段结构
HTTP/1.1 406 Not Acceptable
Content-Type: application/json; charset=utf-8
Vary: Accept

{"error":{"code":"not_acceptable","message":"supported: image/png, image/webp, application/json","request_id":"req_placeholder"}}

落地检查清单#

  1. 每个端点声明一份封闭的媒体类型集合。
  2. Accept 与 Content-Type 成对出现,选不中返回 406 并列出可接受值。
  3. 协商类响应带 Vary: Accept。
  4. 二进制走独立端点,带子类型、ETag 与缓存指令。
  5. 错误体格式与成功响应保持一致,字段名稳定。
  6. 大对象优先条件请求,而不是延长缓存时间。

这些规则本身与具体平台无关,但落到 API.PLUS 上时,各端点实际支持哪些媒体类型、是否返回 Vary、错误码字段叫什么,都需要逐个核对,无法由本文推断。

作者:沐小悠

本文约 6 分钟读完 · 关键词:响应设计、内容协商、Accept 请求头、Vary 响应头、ETag

更多文章 GET 示例接口

「」标签下的其他文章