JSON、表单与图片接口的响应设计:三类端点不要共用一套错误壳
三类端点应分别围绕 JSON 结构、表单提交语义和图片字节设计响应;可以统一错误码,但不宜强推同一 JSON 外壳,并应明确字段错误、内容协商、缓存与上传边界。
三类接口不该共用一套响应模板。JSON 端点服务于程序解析,表单端点服务于浏览器提交与页面回显,图片端点服务于字节读取与缓存;把三者都包成 {code,msg,data},容易破坏 HTTP 语义和缓存行为。更稳妥的做法是:按消费者确定媒体类型,把成功与失败都写进契约,再补内容协商、缓存和安全边界。以下为通用设计建议,不假设 API.PLUS 的具体网关能力;框架行为需按实际版本核实。
成功响应:先固定媒体类型和结构#
| 端点类型 | 成功响应形态 | 契约重点 |
|---|---|---|
| JSON | application/json; charset=utf-8 | 字段稳定;明确 null 与字段缺省;分页使用游标 |
| 表单 | HTML 页面,或 303 重定向到结果页;XHR 场景可返回 JSON | 字段级错误、CSRF、重复提交防护 |
| 图片 | image/* 字节 | Content-Type、ETag、Cache-Control、尺寸与体积限制 |
JSON 成功体不一定需要统一外壳。若跨端点确实需要元数据或错误信息,可以保留一层 data;若端点简单,直接返回资源对象也可行,关键是同一类端点保持一致。顶层数组建议放入 items 字段,便于后续加游标。时间字段建议使用带时区的 ISO 8601 字符串;ID 建议用字符串,避免大整数在前端失真。
表单端点的消费者如果包含浏览器,应优先保证可提交、可重定向、可回填。成功后可 303 重定向到结果页,降低刷新导致的重复提交;验证失败返回 4xx(常见为 422)并回填字段,不要用 200 页面伪装失败。含文件的表单使用 multipart/form-data,大小、字段数量和类型限制应由服务端执行。CSRF 与重复提交防护也应在服务端完成,不能只靠前端禁用按钮。
图片端点通常直接返回字节,不默认把图片 base64 包进 JSON,否则会增加传输体积和客户端解码成本。响应应给出准确的 Content-Type,并用 ETag 或 Last-Modified 支持条件 GET,命中时返回 304。公开图片可使用较长缓存,私有图片则应按权限设置 Cache-Control;更新图片时优先更换带版本号的 URL。
错误响应:可编程,但不要泄露内部信息#
不要用 200 表示业务失败,也不要把所有失败都压成 500。客户端参数问题使用 4xx,服务端异常使用 5xx;未认证与无权限应区分 401 和 403;限流可返回 429,并在允许时给出 Retry-After。错误体建议包含稳定的 code、可读的 message、用于日志关联的 request_id,以及可选的字段级 errors。不要回显堆栈、SQL、密钥或内部路径。
表单错误应能映射到具体字段,图片端点的错误则要先看客户端能否解析 JSON:面向浏览器的请求可返回状态码和简短文本,面向程序调用时可在 Accept 允许时返回 JSON 错误。内容协商一旦启用,应设置 Vary: Accept,并避免依赖内容嗅探。SVG 可能携带脚本,允许上传时需要清洗或隔离,具体策略应按威胁模型核实。
示意示例:错误体字段关系#
下面只是占位示例,未执行;example.com 不是实际请求地址。生产中的状态行和响应头通常由框架生成。
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"code": "VALIDATION_ERROR",
"request_id": "req_demo_001",
"errors": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "邮箱格式不正确"
}
]
}
落地时先写契约测试,至少覆盖成功、字段错误、未授权、限流、上传过大和缓存命中。本文未调用 API.PLUS 端点;具体框架和网关对状态码、内容协商与缓存头的处理,需按实际环境核实。