接口响应设计规范:内容协商、条件请求与二进制边界
面向开发者的响应设计说明:先确定端点允许哪些表示、由谁选择、选不中时如何失败,再处理 JSON、表单与二进制三种响应的边界,最后用 Vary、ETag 与条件请求保证缓存不误用。文中示例为未执行的占位示例,不涉及 API.PLUS 任何端点的实际响应。
- 响应规范的第一层是表示协商,而不是字段命名: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"}}
落地检查清单#
- 每个端点声明一份封闭的媒体类型集合。
Accept与Content-Type成对出现,选不中返回 406 并列出可接受值。- 协商类响应带
Vary: Accept。 - 二进制走独立端点,带子类型、
ETag与缓存指令。 - 错误体格式与成功响应保持一致,字段名稳定。
- 大对象优先条件请求,而不是延长缓存时间。
这些规则本身与具体平台无关,但落到 API.PLUS 上时,各端点实际支持哪些媒体类型、是否返回 Vary、错误码字段叫什么,都需要逐个核对,无法由本文推断。