Api请求响应体规范
统一返回结构、字段命名、时间格式、空值处理
[Requirement] 请求体必须使用 application/json(Content-Type: application/json; charset=utf-8),禁止混用 x-www-form-urlencoded 与 JSON。
[Standard] 文件上传使用 multipart/form-data,且每个上传字段需在文档中标注文件类型与大小限制。
[Requirement] 所有入参必须做类型、范围、必填校验,校验失败返回 400 并指明具体字段。
[Standard] 入参对象与出参对象分离定义(XxxCreateRequest / XxxUpdateRequest / XxxResponse),不复用数据库实体。
# 入参 DTO
class UserCreateRequest(BaseModel):
name: str = Field(..., min_length=1, max_length=50)
email: EmailStr
age: int | None = Field(None, ge=0, le=150)
# 出参 DTO
class UserResponse(BaseModel):
id: int
name: str
email: str
created_at: datetime[Default] 单请求体默认上限 1 MB;超过需使用分块上传或专门的大文件接口,并在网关层拦截超限请求返回 413 Payload Too Large。
[Requirement] 所有业务响应必须使用统一信封结构,包含 code、message、data、trace_id 四个字段。
{
"code": 0,
"message": "ok",
"data": { ... },
"trace_id": "7f3a9c2e1b4d..."
}| 字段 | 类型 | 说明 |
|---|---|---|
| code | int | 业务码,0 表示成功,非 0 表示业务错误(见错误码规范) |
| message | string | 面向调用方的可读消息,成功为 ok |
| data | object/array/null | 业务数据,无数据时为 null |
| trace_id | string | 全链路追踪 ID,便于排查 |
[Caution] HTTP 状态码与 code 字段不可互相替代:HTTP 状态码表达传输层结果,code 表达业务层结果。成功时 HTTP 200 + code: 0;业务错误时 HTTP 4xx + 对应 code。
[Standard] 列表接口的 data 为对象,包含 items 与 pagination,禁止直接返回数组。
{
"code": 0,
"message": "ok",
"data": {
"items": [ ... ],
"pagination": { "page": 1, "page_size": 20, "total": 156, "total_pages": 8 }
},
"trace_id": "..."
}[Caution] 禁止在成功响应中混入 error、success、result 等多余字段,保持结构统一。
// 反模式:字段不统一
{ "status": "success", "result": {...}, "msg": "done" }
{ "success": true, "data": {...}, "error": null }[Requirement] JSON 字段使用 snake_case(下划线小写),与 Python/Go 后端语言风格一致。
[Standard] 布尔字段以 is_ / has_ / can_ 开头,如 is_active、has_avatar、can_edit。
[Standard] ID 字段以资源名 + _id 结尾,如 user_id、order_id;资源自身主键使用 id。
| 场景 | 字段名 |
|---|---|
| 创建/更新时间 | created_at、updated_at |
| 时间范围 | created_from、created_to |
| 数量 | total_count、page_size |
| 金额 | amount、total_amount(单位见下) |
| 枚举 | status、type、level |
[Requirement] 金额字段以分为单位(整数),避免浮点数精度问题;在文档中明确单位。
{ "amount": 1299 } // 表示 ¥12.99[Caution] 禁止使用 float 传输金额,禁止在前端用 toFixed 后再传字符串替代整数。
[Requirement] 所有时间字段使用 ISO 8601 格式,带时区,统一为 UTC:2026-10-14T08:30:00Z。
[Standard] 时间戳字段名以 _at 结尾;纯日期使用 _date 结尾,格式 YYYY-MM-DD。
[Default] 内部存储使用 datetime(带时区),仅在序列化层转为 ISO 字符串,禁止在业务逻辑中传字符串时间。
| 字段类型 | 格式 | 示例 |
|---|---|---|
| 时间戳 | ISO 8601 UTC | 2026-10-14T08:30:00Z |
| 日期 | YYYY-MM-DD | 2026-10-14 |
| 时长 | ISO 8601 Duration | PT1H30M(1.5 小时) |
[Caution] 后端一律以 UTC 存储与传输,前端负责按用户时区展示;禁止后端返回带本地时区偏移的时间(如 +08:00)。
// 反模式
{ "createTime": "2026/10/14 16:30" } // 非 ISO、用斜杠、无时区
{ "created_at": 1728894600 } // 时间戳数字(易混淆秒/毫秒)
{ "created_at": "2026-10-14T16:30:00+08:00" } // 带本地偏移,应统一为 UTC[Requirement] 字段值不存在时返回 null,禁止返回空字符串 "" 或 0 来表示”无”。
[Standard] 区分”字段不存在”与”字段值为 null”:响应中声明过的字段必须出现(值可为 null),未声明字段不出现。
// 正确:无头像时返回 null
{ "id": 1, "name": "张三", "avatar_url": null }
// 错误
{ "id": 1, "name": "张三", "avatar_url": "" } // 用空串表示无
{ "id": 1, "name": "张三" } // 字段缺失[Standard] 列表字段为空时返回 [],不返回 null,以便调用方直接遍历。
{ "tags": [], "roles": [] }[Default] PATCH 请求中,未传字段表示”不修改”,传 null 表示”置空”;二者语义必须区分。
PATCH /api/v1/users/123
{ "nickname": null } // 将 nickname 置空
// 不传 nickname // 不修改 nickname// 反模式
{ "phone": "" } // 无手机号应返回 null
{ "tags": null } // 空集合应返回 []
{ "deleted_at": 0 } // 未删除应返回 null