JSON、表单、图片接口的响应设计规范:判定信号、错误定位与字节流
三类接口应共用一套成功判定信号:HTTP 状态码加内容类型;JSON 用稳定错误码与字段级错误,表单校验失败用 422 并把错误落到字段名,图片接口成功即返回图片字节、失败才返回结构化错误体。文中给出可复用的契约清单和部分成功、限流等边界处理建议。属设计建议,未在具体服务上实测。
三类接口的响应设计,分歧通常不在“返回什么数据”,而在“客户端凭什么判定成功”。建议先定一个跨类型统一的判定信号,再让 JSON、表单、图片各自的响应体只承担它最擅长的事。本文是设计建议,未在具体服务上执行或实测。
一、判定信号:状态码优先,内容类型必须一致#
把 HTTP 状态码作为第一判定信号:成功用 2xx,请求本身有问题用 4xx,服务端故障用 5xx。即使业务拒绝,只要原因是客户端提交的内容,也应落在 4xx,而不是用 200 包住一个 success=false。
第二信号是 Content-Type。JSON 响应就写 application/json,图片响应就写具体的 image/png、image/jpeg,不要用 application/octet-stream 含糊带过。客户端应能只凭这两个信号决定走哪条解析分支。
如果历史接口已经用 200 承载业务失败,至少要在文档中固定一个机器可读的错误字段,并保证它不随文案变化。
二、JSON 接口:错误对象要能被程序消费#
建议错误对象固定包含四类信息:code(稳定、可枚举,发布后不改)、message(给人读,可本地化)、fields(定位到具体参数)、trace_id(与服务端日志关联)。不要把堆栈、SQL 语句、内网主机名放进响应体。
数据表示上,时间用带时区的 ISO 8601 格式(国际标准日期时间表示法),金额用最小货币单位的整数加币种,避免浮点误差。分页若返回总数,要说明它是精确值还是估算值。
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "参数校验失败",
"fields": [{"name": "email", "reason": "格式不合法"}],
"trace_id": "占位值"
}
}
上例为演示用的未执行示例,字段名可按团队约定调整。
三、表单接口:错误必须落到字段#
表单提交常用 application/x-www-form-urlencoded 或 multipart/form-data。传统页面提交成功后用 303 重定向到结果页,可避免刷新导致重复提交;前端用 fetch 提交时,则返回 200 加 JSON。
校验失败建议返回 422,并在 fields 中以表单控件的 name 为键,前端才能就地标注错误。同一个端点不要有时返回 HTML 错误页、有时返回 JSON,可用 Accept 头或固定路径区分两类客户端。
四、图片接口:字节流与元数据分离#
图片请求成功时,响应体就是图片字节,不要再套一层 JSON 再让客户端 base64 解码。失败时才返回 JSON 或 HTML 错误体。客户端应先看 Content-Type,再决定是否解析 JSON,不要对图片响应直接调用 JSON 解析。
- 提供
ETag(实体标签,用于判断资源是否变化)与Last-Modified,支持条件请求,减少重复传输。 - 需要“下载”语义时用
Content-Disposition: attachment; filename=...。 - 图片尚未生成时,用 202 加一个状态查询地址,不要用 200 返回占位字节冒充成品。
五、契约清单#
| 项目 | JSON | 表单 | 图片 |
|---|---|---|---|
| 成功判定 | 2xx + application/json | 303 或 2xx | 2xx + image/* |
| 失败体 | 结构化 error 对象 | 422 + fields | 非 image/* 的错误体 |
| 缓存 | 按资源语义定 | 一般不建议缓存 | ETag / Last-Modified |
| 重复提交 | 幂等键 | 一次性令牌 | 同一地址可重复取 |
六、边界条件#
批量操作不要只返回一个 200 就结束,应给每项结果和顶层汇总。触发限流时用 429 并带 Retry-After,让客户端知道何时重试。以上规则的具体阈值、字段命名和错误码表,需要在团队内部评审后固定,本文不替代实际接口测试。