用户登录

技术文章

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

JSON、表单与图片接口的响应设计规范

一句话回答

文章给出 JSON、表单、图片三类接口的响应设计约定:JSON 用统一四字段信封与稳定错误码,表单按 Accept 做内容协商并返回字段级错误,图片默认返回二进制、需要元数据时切 JSON,并统一携带 request_id 便于追踪。

核心要点 TL;DR
  • JSON 接口统一四字段信封,成功与失败走同一条解析路径
  • 业务码表达具体原因,HTTP 状态码表达语义层结果,不可混用
  • data 类型恒定,空列表返回 [],ID 用字符串避免精度丢失
  • 表单接口按 Accept 做内容协商,校验失败返回 422 与字段级 errors
  • 图片接口默认返回二进制,需要元数据时切 JSON,出错绝不返回 200

引言:响应结构是最容易被 AI 写坏的部分#

这两年接口开发的门槛在快速下降:脚手架、序列化、参数校验、甚至整套 Controller 都能被工具一次性生成。但生成得越快,越容易暴露一个问题——大家写的是能跑的代码,而不是稳定的契约。同一个服务里,有的接口成功时返回对象、失败时返回字符串;有的图片接口出错时返回一段 HTML 报错页;有的表单接口把校验错误塞进 302 跳转的 query 里。这些细节不会让单测失败,却会让调用方(无论是人还是自动化客户端)付出成倍的适配成本。

本文以 API.PLUS 上常见的三类接口形态——JSON 数据接口、表单提交接口、图片二进制接口——为线索,给出一套可以直接抄进团队规范里的响应设计约定。核心目标只有一个:让响应在任何分支下都是可预测、可机读、可降级的。

一、先立契约,再写实现#

响应设计不是编码完成后的收尾工作,它应该和接口路径一起被确定下来。建议在动手前先回答四个问题:

  • 成功与失败是否共用同一个外层结构?如果不共用,客户端就要写两套解析逻辑。
  • 错误信息是给人看的,还是给程序判断的?两者必须同时存在,缺一不可。
  • 同一字段的类型是否永远不变?今天返回数组、明天返回 null,是联调事故的高发区。
  • 出问题时能不能只靠一个 ID 定位到日志?没有 request_id,线上排查基本靠猜。

把这四条写进接口文档,比写十页使用说明更有价值。API.PLUS 的接口文档在这一点上做得比较克制,例如 IP 查询接口 与 POST 示例接口,参数、返回、错误示例都在同一页内,便于调用方一次性对齐预期。

二、JSON 接口:统一信封 + 稳定错误码#

JSON 是最容易被设计得五花八门的格式。推荐使用固定四字段信封,让成功与失败走同一条解析路径:

示例说明:以下代码仅用于逻辑验证,生产环境需补充异常处理、超时与重试、日志与监控告警及降级策略。

{
  "code": 0,
  "message": "ok",
  "request_id": "req_9f1c2a7b",
  "data": {
    "ip": "8.8.8.8",
    "country": "United States",
    "isp": "Google LLC"
  }
}

失败时只改变 code、message 与 data 的取值,字段本身永远存在:

{
  "code": 40001,
  "message": "invalid parameter: ip",
  "request_id": "req_9f1c2a7b",
  "data": null
}

几个容易踩坑的地方值得单独强调:

  • 业务码与 HTTP 状态码分工明确。HTTP 状态码表达传输层与语义层结果(200 / 400 / 401 / 429 / 500),业务码表达具体原因。不要用 200 + code=500 糊弄监控系统。
  • data 的类型必须恒定。列表为空时返回 [] 而不是 null;单对象缺失时返回 null 而不是 {}。类型抖动比字段缺失更难处理。
  • ID 一律用字符串。超过 2^53 的整数在前端会被静默截断,这类 bug 极难复现。
  • 时间统一 ISO 8601 并带时区,例如 2026-10-28T09:30:00+08:00,不要返回时间戳与格式化字符串混用。
  • 布尔值不要用 0/1。不同语言的弱类型转换行为不一致,前端 if ("0") 永远为真。

调用一个返回 JSON 的接口时,可以顺手用 Accept 头声明期望格式,多数实现会据此选择序列化方式:

curl -s -H "Accept: application/json" \
  "https://www.api.plus/api/get?apikey=YOUR_KEY&ip=8.8.8.8"

注意 apikey 属于凭证,不应出现在前端日志、错误上报或截图里。生产环境建议由服务端代理调用,前端只拿到必要字段。

三、表单接口:内容协商与字段级错误#

表单接口(application/x-www-form-urlencoded 与 multipart/form-data)长期存在一个误区:因为浏览器默认会渲染 HTML,所以错误也用 HTML 表达。这在 2026 年已经不太成立——调用表单接口的往往不是浏览器表单,而是移动端、脚本或工作流编排工具。

推荐的处理方式是按 Accept 分流:

  • Accept: application/json 时,无论成功失败都返回 JSON 信封,字段错误放进 data.errors 数组,每项包含 field 与 reason。
  • 浏览器直连时返回 HTML 页面,但要使用正确的语义状态码(校验失败 422,未登录 401),而不是一律 200。
  • 不要把错误信息拼进 302 的 Location,重定向会丢失请求体上下文,日志里只剩下一串 URL。
{
  "code": 42201,
  "message": "validation failed",
  "request_id": "req_5b8e1d40",
  "data": {
    "errors": [
      { "field": "email", "reason": "invalid format" },
      { "field": "name", "reason": "required" }
    ]
  }
}

另外两点经常被忽略:其一,涉及写操作的表单接口应当具备幂等语义,重复提交不能产生多条记录,可用客户端传入的幂等键配合唯一索引兜底;其二,文件上传字段要与普通字段分开限制大小,超限时返回明确的 413 而非连接被重置。具体字段约定可参照 POST 示例接口文档 的说明。

四、图片接口:字节流与元数据的双通道#

图片接口的特殊性在于它有两种合理的返回形态,混用就会出问题。业界比较通行的做法是默认返回二进制,需要元数据时显式切换:

  • 默认响应头为 Content-Type: image/jpeg(或实际格式),响应体即图片字节,便于 <img> 直接引用。
  • 通过 type=json 之类的参数或 Accept: application/json 切换为 JSON 信封,返回图片 URL、尺寸、格式、过期时间等元数据。

API.PLUS 的 随机动漫壁纸接口 就提供了这种切换能力,例如:

# 直接拿二进制图片
curl -s -o wallpaper.jpg "https://www.api.plus/api/dongman"

# 拿 JSON 元数据,便于前端做预加载与占位图
curl -s "https://www.api.plus/api/dongman?type=json"

图片接口最容易犯的三个错误:

  1. 出错时返回 200 + HTML。浏览器会把错误页当成图片缓存下来,用户看到的是持续破图。图片通道出错必须返回非 200 状态码,且响应体为纯文本或 JSON。
  2. 缺少缓存头。随机图片可以标记 Cache-Control: no-store,固定资源则应给出 max-age 与 ETag,并正确响应 If-None-Match 返回 304。
  3. 不返回 Content-Length。缺少长度信息会让部分客户端无法展示进度条,也会影响反向代理的缓冲策略。
HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 284613
Cache-Control: no-store
ETag: "dm-7c31a9"
X-Request-Id: req_2d6f8b11

如果业务确实需要用 JSON 传图片内容,优先返回可访问的 URL 而不是 base64。base64 会让体积膨胀约 33%,并且无法享受浏览器与 CDN(Content Delivery Network,内容分发网络) 的图片缓存。

五、可观测性:让每个响应都能被追查#

响应规范的最后一块拼图是追踪能力。建议在所有形态的响应中都带上 X-Request-Id 响应头(JSON 接口同时在体内回传 request_id),并在服务端日志中以该 ID 为主键串联上下游调用。这样用户报障时只需提供一个 ID,就能定位到具体参数、耗时与后端依赖状态。

错误码也需要一份可维护的登记表,避免相邻模块各自编号导致语义冲突:

业务码HTTP 状态含义客户端建议动作
0200成功正常解析 data
40001400参数缺失或格式错误修正参数后重试
40101401API Key 无效或缺失检查凭证,不要重试
42901429触发限流按 Retry-After 退避
50001500服务端内部错误携带 request_id 反馈

限流响应的 Retry-After 头尤其值得实现,它能让客户端从“盲目重试”变成“有节奏重试”,显著降低雪崩风险。相关的设计思路可以在 API 文章中心 找到更完整的讨论。

六、落地检查清单与总结#

把上面的约定压缩成一份评审清单,新接口上线前逐条打勾即可:

  • 成功与失败共用同一外层信封,字段始终存在,data 类型恒定;
  • 业务码与 HTTP 状态码各自表达不同层次的信息,错误码有登记表;
  • 所有响应携带 X-Request-Id,服务端日志可按其检索;
  • 表单接口按 Accept 做内容协商,校验失败返回 422 与字段级错误;
  • 图片接口默认返回二进制并设置正确的 Content-Type、Content-Length 与缓存头,出错时绝不返回 200;
  • 限流返回 429 并附带 Retry-After。

总结一下:在代码可以被大量自动生成的今天,接口的价值越来越集中在“契约是否稳定”上。JSON 接口靠统一信封与稳定错误码取胜,表单接口靠内容协商与字段级错误取胜,图片接口靠二进制与元数据的清晰分离取胜。三者共同的前提是——响应结构先于实现被定义,并且在所有分支下保持一致。

想快速验证这套规范,可以直接在 API.PLUS 注册账号获取免费 API Key,用 IP 归属地查询接口 观察标准 JSON 响应,用 随机动漫壁纸接口文档 体会二进制与 JSON 两种返回形态,再结合 POST 请求示例接口 对齐表单约定。遇到具体的字段或状态码疑问,可通过 技术支持 反馈。

常见问题(FAQ)

JSON 接口成功和失败应该用不同的返回结构吗?
不应该。推荐固定四字段信封 code、message、request_id、data,失败时只改变这些字段的取值,字段本身永远存在,data 置为 null。这样客户端只需一套解析逻辑,成功与失败走同一条路径。
业务码和 HTTP 状态码应该怎么分工?
HTTP 状态码表达传输层与语义层结果,例如 200、400、401、429、500;业务码表达具体原因,例如 40001 表示参数缺失或格式错误。不要用 200 加 code=500 的方式糊弄监控系统。
表单接口校验失败时应该返回什么?
按 Accept 分流:声明 application/json 时返回统一 JSON 信封,字段错误放进 data.errors,每项含 field 与 reason;浏览器直连可返回 HTML,但要用 422 等正确状态码,不要把错误拼进 302 的 Location。
图片接口出错时能返回 HTML 错误页吗?
不能。图片通道出错必须返回非 200 状态码,响应体为纯文本或 JSON。否则浏览器会把错误页当成图片缓存下来,用户看到的是持续破图,排查也很困难。
线上排查问题如何只靠一个 ID 定位日志?
让所有响应都带 X-Request-Id 响应头,JSON 接口同时在体内回传 request_id,服务端日志以该 ID 为主键串联上下游调用。用户报障时只需提供一个 ID,即可定位具体参数、耗时与后端依赖状态。

作者:沐小悠

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

更多文章 GET 示例接口

「」标签下的其他文章