本文目录导读:

标准化API的成功与错误响应是提升系统可维护性、降低前后端协作成本的关键,目前业界最通用且成熟的规范是 JSON:API 或基于 RESTful 风格的自定义规范。
以下是推荐的标准化方案,结合了RFC 7807(Problem Details for HTTP APIs)和常见的最佳实践。
核心原则
- 统一结构:无论是成功还是错误,响应体都遵循相同的顶层字段结构。
- HTTP状态码 + 业务码:HTTP状态码表示传输层状态(如200, 400, 500),业务码(
code)表示业务逻辑状态(如1000表示成功,2001表示用户不存在)。 - 自描述性:错误信息要足够具体,能帮助调用方确定问题所在。
推荐的标准响应结构
采用一个通用的ApiResponse对象,包含四个关键字段:
| 字段名 | 类型 | 是否必须 | 说明 |
|---|---|---|---|
success |
boolean |
是 | 业务处理是否成功。true 或 false。 |
code |
string/int |
是 | 业务状态码,成功时固定(如"OK"或1000),失败时为具体错误码。 |
message |
string |
是 | 对本次响应的简短、人类可读的描述。 |
data |
object/null |
是 | 承载实际数据的容器,成功时必须包含数据;失败时为null或错误详情。 |
errors |
array |
推荐 | (新增字段) 详细错误列表,用于表单验证等场景。 |
成功响应的标准化
HTTP状态码:200 OK、201 Created、204 No Content。
示例1:获取单个资源(200)
{
"success": true,
"code": "OK",
"message": "用户信息查询成功",
"data": {
"id": 123,
"name": "张三",
"email": "zhangsan@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
"errors": null
}
示例2:创建资源成功(201)
{
"success": true,
"code": "CREATED",
"message": "订单创建成功",
"data": {
"order_id": "ORD-20241015-0001",
"status": "pending"
},
"errors": null
}
示例3:列表查询(200)
建议在data中包含分页元信息,而非平铺。
{
"success": true,
"code": "OK",
"message": "查询成功",
"data": {
"items": [
{ "id": 1, "title": "文章1" },
{ "id": 2, "title": "文章2" }
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 58,
"total_pages": 3
}
},
"errors": null
}
错误响应的标准化
核心原则:区分系统级错误(如数据库连接失败)和业务级错误(如余额不足、参数校验失败)。
通用错误结构(遵循 RFC 7807 风格)
{
"success": false,
"code": "PARAM_INVALID",
"message": "请求参数校验失败",
"data": null,
"errors": [
{
"field": "email",
"type": "invalid_format",
"message": "邮箱格式不正确"
},
{
"field": "age",
"type": "range_error",
"message": "年龄必须在18-60之间"
}
]
}
常见HTTP状态码与业务码映射
| 场景 | HTTP状态码 | 业务码 (code) |
示例 message |
|---|---|---|---|
| 请求成功 | 200/201 | OK / CREATED |
处理成功 |
| 参数错误 | 400 | PARAM_INVALID |
请求参数验证失败 |
| 未授权 | 401 | UNAUTHORIZED |
令牌无效或已过期 |
| 无权限 | 403 | FORBIDDEN |
当前用户无操作权限 |
| 资源未找到 | 404 | NOT_FOUND |
用户ID 123 不存在 |
| 请求冲突 | 409 | RESOURCE_CONFLICT |
邮箱已被注册 |
| 依赖失败 | 422 | UNPROCESSABLE_ENTITY |
业务规则不满足(如余额不足) |
| 请求频次超限 | 429 | RATE_LIMIT_EXCEEDED |
请求过于频繁,请稍后重试 |
| 服务器内部错误 | 500 | INTERNAL_ERROR |
服务器内部异常,请联系管理员 |
| 服务不可用 | 503 | SERVICE_UNAVAILABLE |
服务维护中 |
注意:永远不要将服务器内部异常栈(如Java StackTrace)暴露给客户端!使用通用message代替。
特殊场景处理
分页与列表
- 错误案例:把分页信息放在
errors里。 - 正确做法:如上文,分页元数据放在
data.pagination内。
文件上传
- 成功:返回文件URL和元数据。
- 失败:返回具体的字段错误,如
file.too_large、type.unsupported。
异步操作(202 Accepted)
对于耗时操作,可以返回202 Accepted状态码:
{
"success": true,
"code": "ACCEPTED",
"message": "任务已提交,正在处理",
"data": {
"task_id": "task_abc123",
"status_url": "/tasks/task_abc123/status"
},
"errors": null
}
最佳实践总结
- 一致性:整个API的所有端点必须遵循同一结构,不允许成功返回
data,失败返回error这种不一致的设计。 - 拒绝裸数组:永远不要在顶层直接返回一个数组 ,顶层必须是
data对象。 - 统一错误码管理:建立中心化的错误码字典(如枚举或常量类),避免散落在代码各处。
- 国际化:
message字段建议支持占位符,并预留多语言切换的能力(可通过Accept-Language头部或统一管理)。 - 日志链路:在
errors或根层级中,可以附加一个trace_id用于内部排障,但不向外暴露堆栈。 - 框架集成:
- Spring Boot:使用
@ControllerAdvice+ResponseEntityExceptionHandler全局处理。 - Django:使用
django-rest-framework的exception_handler+ 自定义响应格式化。 - Node.js (Express):使用中间件统一包装
res.success()和res.fail()。
一个完整的代码示例(伪代码)
# Python / Flask 示例
def success_response(data=None, message="操作成功", code="OK", http_status=200):
return {
"success": True,
"code": code,
"message": message,
"data": data,
"errors": None
}, http_status
def error_response(message="请求失败", code="ERROR", errors=None, http_status=400):
if errors is None:
errors = []
return {
"success": False,
"code": code,
"message": message,
"data": None,
"errors": errors
}, http_status
# 使用
@app.route('/users', methods=['POST'])
def create_user():
try:
user = create_user_service(request.json)
return success_response(data=user, message="用户创建成功", code="CREATED", http_status=201)
except ValidationError as e:
return error_response(message="参数校验失败", code="PARAM_INVALID", errors=e.errors, http_status=400)
except Exception:
return error_response(message="服务器内部错误", code="INTERNAL_ERROR", http_status=500)
通过以上标准化方案,你的API将具备高度的可预测性、可调试性和兼容性,能够很好地支持前端、移动端和第三方调用的需求。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播