不能直接 return dict,因Flask虽自动jsonify顶层dict/list,但嵌套自定义对象(如ORM模型、datetime、Decimal)会抛TypeError;且手动拼接{"code":0,"data":...,"msg":"ok"}易漏字段、状态码不一致、命名混乱(msg/message),导致前端兼容成本高。

为什么不能直接 return dict?
Flask 默认返回字典时会调用 jsonify(),但仅限于顶层是 dict 或 list;如果中间嵌套了自定义对象(比如 ORM 模型实例)、datetime、Decimal,就会抛出 TypeError: Object of type X is not JSON serializable。更关键的是,不同接口手动拼 {"code": 0, "data": ..., "msg": "ok"} 容易漏字段、错状态码、不一致——比如有的写 message,有的写 msg,前端得写一堆兼容逻辑。
用 @app.after_request 统一包装响应体
这是最轻量、侵入性最小的方式,适合已有项目快速收敛格式。核心思路:拦截所有 application/json 响应,只处理未被标记为“已格式化”的响应体。
- 在视图函数里返回原始数据前,加个标记,比如设置
response.is_api_response = True - 在
@app.after_request中检查这个标记,若存在且响应 Content-Type 是 JSON,则用标准结构包裹response.get_json(),再重新序列化 - 注意:不能直接修改
response.data字符串,要调用jsonify()重建响应对象,否则中文乱码或 headers 错乱
@app.after_request
def format_api_response(response):
if getattr(response, 'is_api_response', False) and 'application/json' in response.content_type:
try:
original_data = response.get_json()
formatted = {
"code": 0,
"data": original_data,
"msg": "success"
}
return jsonify(formatted)
except Exception:
pass # 非法 JSON 不处理,避免雪崩
return response
封装统一的 success() / fail() 工具函数
比装饰器更可控,也更容易测试。重点不是“多写一个函数”,而是强制约束返回路径——所有 API 接口必须通过这两个函数退出。
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
-
success(data=None, msg="success", code=0):自动处理datetime、Decimal等常见非序列化类型(用default参数传给json.dumps) -
fail(msg="error", code=-1, data=None):错误码和消息分离,便于前端 switch code 做差异化提示 - 不要在工具函数里做日志或鉴权,那是中间件的事;这里只干一件事:序列化 + 包装
- 避免把数据库对象直接传给
data,先转成 dict 或用 schema 序列化(如model.to_dict())
from flask import jsonify
from decimal import Decimal
import json
from datetime import datetime
<p>def json_default(obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, Decimal):
return float(obj)
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")</p><p>def success(data=None, msg="success", code=0):
return jsonify({
"code": code,
"data": data,
"msg": msg
}), 200, {'Content-Type': 'application/json; charset=utf-8'}</p><p>def fail(msg="error", code=-1, data=None):
return jsonify({
"code": code,
"data": data,
"msg": msg
}), 200, {'Content-Type': 'application/json; charset=utf-8'}
别忽略 HTTP 状态码和业务码的分工
很多团队只用 code 字段做全部判断,结果导致 401 和 403 全部返回 {"code": -1},前端无法区分是登录过期还是权限不足。HTTP 状态码该用还得用。
立即学习“Python免费学习笔记(深入)”;
- 认证失败(token 过期/无效)→ 401 +
{"code": 401, "msg": "Unauthorized"} - 权限不足 → 403 +
{"code": 403, "msg": "Forbidden"} - 参数校验失败 → 400 +
{"code": 1001, "msg": "Invalid param"}(自定义业务码) - 后端异常 → 500 +
{"code": 500, "msg": "Internal error"},但生产环境建议隐藏细节 - 前端应优先检查 HTTP 状态码,再看
code字段做细化处理
真正难的不是写这几个函数,而是让所有人——包括新来的同事、临时顶班的测试同学——都记得调用 success() 而不是直接 return {"data": ...}。最好配个 pre-commit hook,扫描 return \{.*\} 模式并警告。

















