用户登录

技术文章

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

JSON、表单与图片接口的响应规范:成功形态、错误契约与缓存头怎么分别定

一句话回答

三类端点的成功响应形态不同:JSON 返回可解析结构,浏览器表单返回重定向或 HTML,图片返回字节流。因此错误契约应按端点类型分别定义:JSON 与 API 表单用结构化错误体,浏览器表单返回 HTML 或重定向,图片端点出错时不得返回图片占位内容并配 200 状态码。HTTP 状态码是唯一的主状态通道,业务 code 不应与之并行表达同一含义。

核心要点 TL;DR
  • 成功响应是结构还是字节流,决定了错误响应能否复用同一个壳。
  • HTTP 状态码应作为主状态;再叠加业务 code 会让客户端出现两套判断。
  • 图片端点出错时返回 JSON 错误体,不要返回占位图加 200。

三类端点不应该共用一套响应格式。JSON 端点的成功体需要可解析的结构,浏览器表单的成功响应往往是一次重定向,图片端点的成功体是字节流。如果强行把它们塞进同一个 {code, msg, data} 包装,每一类调用方都得额外拆一层,图片还要先判断响应到底是不是图片。

先定四条共同约定#

  • 状态码是权威结果:HTTP 状态码表达成功、客户端错误或服务端错误,响应体里的业务码只能是补充,不能与它冲突。
  • 错误体结构稳定:字段名和类型不随具体错误变化,客户端才能写一个通用分支。
  • 内容协商明确:请求的 Accept 决定返回 JSON 还是 HTML,服务端不为无法满足的类型伪造 200。
  • 缓存语义写进文档:哪些响应可缓存、靠什么校验,要和实际返回的头一致。

JSON 接口:成功体就是资源本身#

成功时直接返回资源或集合,不要在外面再包一层 data。分页信息可以放在响应顶层(如 items 与 next_cursor),也可以用 Link 头,但必须二选一后写进文档。

错误体建议采用 Problem Details 的字段集:type、title、status、detail、instance。相关规范(RFC 9457 等)的编号与最新状态本文未联网核验,落地前请查官方文档。

// 未执行的示例,仅示意字段结构
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://example.com/problems/validation-error",
  "title": "Validation failed",
  "status": 422,
  "detail": "email 字段格式不合法",
  "instance": "/v1/users",
  "errors": [
    { "field": "email", "code": "invalid_format" }
  ]
}

表单接口:先分清是谁在提交#

浏览器表单与脚本调用是两种不同的调用方,响应设计不应混用。

  • 浏览器提交:成功后用 303 重定向到结果页,避免刷新重复提交;校验失败返回 422 并渲染带字段级错误的页面,而不是跳转到一个通用错误页。
  • 脚本提交(application/x-www-form-urlencoded 或 multipart/form-data):成功和失败都返回 JSON,与 JSON 端点共用同一套错误体。
  • multipart 上传:部分文件失败时,要么整体失败并列出失败项,要么返回 207 并在体内逐项标注结果,不要静默丢弃。

图片接口:成功是字节流,失败不是#

图片接口最常见的错误是在失败时返回一张占位图并配 200。这会让调用方的重试和缓存逻辑都失效。

  • 成功:200 加准确的 Content-Type(image/png、image/webp 等),不要靠客户端猜格式。
  • 失败:返回 4xx 或 5xx,错误体用 JSON;被 <img> 引用时浏览器不会展示错误体,这不构成返回图片的理由。
  • 缓存:给出 ETag 或 Last-Modified,并正确处理 If-None-Match 返回 304;带 Content-Length 更利于客户端判断截断。
  • 格式协商:根据 Accept 在 webp、avif、png 之间选择,无法满足时降级到服务端确实支持的格式,并在响应头体现实际格式。

三类端点对照#

维度JSON表单图片
成功体结构化资源重定向或 JSON字节流
错误体problem+jsonHTML 或 problem+jsonproblem+json
失败状态码4xx / 5xx422、303 组合4xx / 5xx
缓存依据按资源语义通常不缓存ETag / Cache-Control

边界与待核实#

错误体加字段属于兼容变更,修改字段含义或状态码映射不属于。旧客户端的迁移窗口、网关是否会改写 Content-Type、CDN 对 304 的处理方式,都需要在实际链路上核实,本文未做此类测试。具体规范的版本状态以官方发布为准。

常见问题(FAQ)

JSON 接口还要不要返回 {code, msg, data}?
如果 HTTP 状态码已经表达了成功与失败,再放一个语义相同的 code,就会造成两套真相。更稳妥的做法是:状态码表达结果类别,响应体只承载资源或错误详情。已有客户端依赖 code 时,保留它作为兼容字段即可,但要在文档里写明哪一个是权威来源。
图片接口返回 404 时应该给 JSON 还是空响应?
取决于调用方。给浏览器 img 标签用时,错误体基本不会被展示;给脚本或 SDK 用时,JSON 错误体更易解析。常见做法是按 Accept 头协商:接受 application/json 的请求返回结构化错误,其余情况返回空体加正确状态码,两种情况都保留同一状态码。

作者:沐小悠

本文约 5 分钟读完 · 关键词:API 响应设计、JSON 接口错误格式、表单接口响应、图片接口 Content-Type、Problem Details

更多文章 GET 示例接口

「」标签下的其他文章