用户登录

技术文章

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

JSON、表单与图片接口的响应设计规范:三类端点的取舍与边界

一句话回答

三类接口不该共用一套万能返回体:JSON 端点要固定成功形态并给出字段级错误对象;表单端点要区分浏览器提交与 fetch 调用,成功跳转、失败回填并前置拦截超大载荷;图片端点要以二进制为主体,把 Content-Type、长度、缓存与 ETag 当作契约,出错时必须用非 2xx 状态码而不是 200 加 JSON。文章给出状态码对照表、兼容性规则与未执行示例,并说明参考素材未覆盖该主题、不含实测数据。

核心要点 TL;DR
  • 一个端点只保留一种成功响应形态,错误形态单独定义并在文档中写死。
  • 状态码表达类别,响应体表达原因与下一步;两者不能互相替代。
  • 图片接口最大的坑是用 200 返回 JSON 错误体,调用方无法区分成功与失败。

先说结论:JSON、表单、图片这三类接口不该共用一套“万能返回体”。可维护的做法是守住三条底线——一个端点只有一种成功形态;状态码负责说明“发生了什么类别的问题”,响应体负责说明“为什么、接下来怎么办”;Content-Type 属于契约的一部分,不能被当成实现细节随手改。

需要提前说明:本次提供的参考素材集中在模型规范、DDoS 报告、开发者平台动态等方向,没有覆盖 API 响应设计这一主题,因此下文属于基于通用 HTTP 语义与工程实践的设计建议,不含实测数据、性能数字或对具体产品的评测。涉及阈值、缓存收益、压缩比之类需要按自身流量核实的部分,文中标为待核实。

一、先把三类端点的共同约束定下来#

无论返回 JSON 还是图片字节,以下三点都成立,且一旦破坏,客户端代码会立刻变得脆弱。

  • 成功形态唯一。同一个端点,成功时要么总是返回对象,要么总是返回数组,不要“有数据返回数组、没数据返回空字符串”。
  • 错误可程序化判断。调用方应当能只靠状态码加一个稳定字段(如 error.code)决定分支,而不是去匹配 message 里的自然语言。
  • 声明解析方式。JSON 带 charset,图片带准确的 Content-Type,表单区分 application/x-www-form-urlencoded 与 multipart/form-data。靠猜测 MIME 类型是常见的线上故障来源。

二、JSON 接口:信封、错误对象与值语义#

要不要包一层信封

裸对象(直接返回业务字段)读起来最省事,但后续想加分页游标、request_id、弃用警告时就没有位置了。信封式结构给它留了空间:

{
  "data": { "id": "item_demo_01", "name": "示例资源" },
  "meta": { "request_id": "req_demo_0001" }
}

(以上为未执行的示例,字段与取值均为占位。)两种风格都能用,真正的风险是中途切换:上线后再把裸对象改成信封,等于对所有调用方做破坏性变更。选一种,然后在文档里固定下来。

错误对象的最小字段集

建议至少包含四项:稳定的机器可读 code、面向人的 message、出错位置(字段名或参数名)、可追踪的 request_id。表单类接口还需要字段级错误集合,才能支持前端逐项回填。

HTTP/1.1 422 Unprocessable Content
Content-Type: application/json; charset=utf-8

{
  "error": {
    "code": "invalid_field",
    "message": "email 格式不合法",
    "field": "email",
    "request_id": "req_demo_0002"
  }
}

(未执行示例,仅示意结构。)注意 code 一旦对外发布就应当视为契约:可以新增取值,但不要给已有取值换含义。

值语义:null、缺省与空集合

三种情况要区分清楚并写进文档:字段值为空(null)、字段未返回(缺省)、集合有零个元素([])。很多客户端 bug 来自把“缺省”当成“空值”处理。单个资源返回对象、列表端点返回数组或带游标的信封,都是可接受的选择,但要一贯。

三、表单接口:成功跳转、失败回填、超限前置拦截#

表单端点通常服务两类调用方:浏览器直接提交,和脚本用 fetch 提交。这两类对响应的期待完全不同。

  • 浏览器直提:成功后用 303 跳转到结果页,避免刷新重复提交;校验失败返回 4xx 并渲染带错误提示与原值的 HTML。
  • 脚本提交:按 Accept 或独立端点返回 JSON 错误体,字段级错误与 JSON 接口共用同一结构,减少两套解析逻辑。

上传类表单还有两个容易被忽略的点:一是文件体积上限要在读完全部请求体之前判断,直接返回 413,而不是先落盘再拒绝;二是 multipart/form-data 的边界解析失败要返回 400 并说明是哪一部分不合法,否则排查成本很高。具体的体积上限与超时值取决于业务与带宽,属于待按自身情况核实的参数。

四、图片接口:二进制是主体,响应头是契约#

图片端点最常见的错误,是出错时用 200 返回一段 JSON。浏览器把这段字节当图片解码,结果是一张破图,而调用方的监控看到的是成功,故障被静默掉了。

响应头作用常见疏漏
Content-Type声明实际图片格式,如 image/webp一律写 image/jpeg,与实际编码不符
Content-Length让客户端预知体积,便于进度与截断检测流式输出时缺失,客户端无法判断是否收全
Cache-Control表达可缓存性与新鲜期随机图端点误加长缓存,导致重复请求拿到同一张
ETag / Last-Modified支持条件请求,命中时返回 304 省流量ETag 随进程重启变化,条件请求永远不命中
Vary按 Accept 协商格式时区分缓存做了格式协商却没有 Vary,缓存串味
HTTP/1.1 200 OK
Content-Type: image/webp
Content-Length: 48213
Cache-Control: public, max-age=86400
ETag: "demo-etag-v3"
Vary: Accept

(此处为二进制字节,示例未实际传输)

(未执行示例,头部取值均为占位。)至于出错时的响应体,有两种可选策略:返回非 2xx 加简短 JSON 错误体,便于程序调用方判断;或者返回固定的占位图加 200,让 <img> 场景不至于出现破图。后者代价是调用方失去区分能力,因此必须写进文档并从监控上单独埋点,不能默认别人知道。

五、状态码与响应形态对照#

下表是一份可直接拿去评审的对照草案,具体取舍以团队文档为准。

状态码语义建议响应体
200成功约定的成功形态(JSON 或图片字节)
201资源已创建新资源表示,或配合 Location
204成功但无内容空体,不返回 JSON
304条件请求未修改空体
400请求语法或参数结构错误结构化错误体
401 / 403未认证 / 已认证但无权限结构化错误体,不回显敏感信息
404资源不存在结构化错误体
409状态冲突(如重复创建)结构化错误体,含冲突字段
413载荷超过上限结构化错误体,说明上限
415媒体类型不受支持结构化错误体,列出可接受类型
422语义校验失败字段级错误集合
429触发限流错误体加 Retry-After
5xx服务端问题通用错误体,暴露 request_id 而非堆栈

六、兼容性:加字段、扩枚举、别动默认形态#

  • 新增字段默认安全,但客户端应被明确告知“忽略未知字段”;反过来,服务端不要假设客户端会读新字段。
  • 错误 code 与枚举型字段只能增量扩展,客户端必须为未知取值准备兜底分支。
  • 默认响应形态(裸对象 vs 信封、图片 vs JSON)一旦公开就不该改;确需变更时用新路径或显式版本参数并行一段时间。
  • 鉴权失败与限流响应也要走同一个错误结构,否则调用方的重试逻辑会出现分叉。示例中的密钥一律用 YOUR_KEY 之类占位符,不要在任何示例或日志里出现真实凭证。

七、边界与待核实项#

上述内容属于设计建议,不是强制性标准,也不构成对任何具体实现的评价。以下几点需要以自己的环境为准核实:不同客户端(浏览器 <img>、移动端图片库、CDN)对 4xx 图片响应的具体表现;随机图端点的缓存策略与去重收益;表单体积上限与超时的合理取值;以及条件请求在你们的部署方式下能否稳定命中。这些都需要可核验的测试记录才能给出结论,本文没有提供,也不应被当作实测结果引用。

常见问题(FAQ)

JSON 接口一定要包 data/meta 信封吗?
不一定。信封的价值在于给元信息(分页、request_id、警告)留位置,代价是多一层嵌套。关键是同一产品内保持一致,并且不要在版本演进中从裸对象改成信封。
表单提交失败应该返回 200 加错误提示,还是 4xx?
面向浏览器的表单页可以返回 4xx 并渲染带错误提示的 HTML;面向 fetch/JSON 调用方则返回 4xx 加结构化错误体。混用会让调用方难以判断,应按 Accept 或端点前缀区分。
图片接口出错时返回占位图可以吗?
可以,但必须写进文档并固定状态码策略,否则调用方无法区分“图不存在”和“图就是这样”。对程序调用方更稳的做法是返回非 2xx 加简短错误体。

作者:沐小悠

本文约 9 分钟读完 · 关键词:API 响应设计、JSON 接口规范、表单接口、图片接口、错误对象

更多文章 GET 示例接口

「」标签下的其他文章