介绍
在国内后端开发中,几乎所有公司都遵循一套固定响应规范:
无论成功还是失败,HTTP 状态码永远返回 200;通过自定义业务码 code 判断请求状态。
而 FastAPI 默认返回格式杂乱、报错会直接抛出堆栈信息、不方便前端统一处理。
本文记录一套最简单、最标准、企业通用的写法:
✅ 成功统一返回格式
✅ 业务错误直接 raise 抛出,不用手写 JSON
✅ 全局异常捕获,屏蔽后端报错堆栈
✅ 完全符合国内前后端交互规范
设计思想
1、错误逻辑:抛出异常,统一处理
只要接口不符合业务条件,不要 return 错误 json。
直接:raise HTTPException\(业务码, 错误信息\)
由全局异常处理器统一封装返回结构。
2、正常逻辑:直接返回数据
业务正常时,调用封装好的成功函数,直接返回标准化数据。
3、国内规范铁律
HTTP 状态码一律 200,code 控制业务状态。
案例
全局HTTPException异常捕获
处理 HTTPException 异常
# ------------------------------
# 全局异常处理器:捕获主动抛出的业务异常(HTTPException异常)
# ------------------------------
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc: HTTPException):
"""
统一处理手动抛出的业务异常
国内规范:HTTP状态码永远200,code为自定义业务码
"""
return JSONResponse(
status_code=200,
content={
"code": exc.status_code,
"message": exc.detail,
"data": None
}
)
全局未知异常处理器
处理 非 HTTPException 异常
# ------------------------------
# 全局未知异常(非HTTPException异常)处理器:捕获代码bug、崩溃、报错
# ------------------------------
@app.exception_handler(Exception)
async def general_exception_handler(request, exc):
"""服务器未知错误,屏蔽堆栈信息"""
return JSONResponse(
status_code=200,
content={
"code": 500,
"message": "服务器内部错误",
"data": None
}
)
完整代码
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
app = FastAPI()
# ------------------------------
# 全局异常处理器:捕获主动抛出的业务异常(HTTPException异常)
# ------------------------------
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc: HTTPException):
"""
统一处理手动抛出的业务异常
国内规范:HTTP状态码永远200,code为自定义业务码
"""
return JSONResponse(
status_code=200,
content={
"code": exc.status_code,
"message": exc.detail,
"data": None
}
)
# ------------------------------
# 全局未知异常(非HTTPException异常)处理器:捕获代码bug、崩溃、报错
# ------------------------------
@app.exception_handler(Exception)
async def general_exception_handler(request, exc):
"""服务器未知错误,屏蔽堆栈信息"""
return JSONResponse(
status_code=200,
content={
"code": 500,
"message": "服务器内部错误",
"data": None
}
)
# ------------------------------
# 统一成功返回工具函数
# ------------------------------
def success_response(data=None, message: str = "请求成功"):
"""成功统一返回格式"""
return {
"code": 200,
"message": message,
"data": data
}
业务接口写法
业务错误:直接抛异常
@app.get("/user/{user_id}")
async def get_user(user_id: int):
# 不满足条件:直接抛异常,不用写return
if user_id <= 0:
raise HTTPException(status_code=400, detail="用户ID不合法")
# 模拟查询用户
user_info = {
"id": user_id,
"username": "测试用户",
"age": 22
}
# 正常返回:调用成功封装函数
return success_response(user_info)
最终返回格式
1、成功响应示例
{
"code": 200,
"message": "请求成功",
"data": {
"id": 10,
"username": "测试用户",
"age": 22
}
}
2、业务错误响应示例
{
"code": 400,
"message": "用户ID不合法",
"data": null
}
3、服务器未知错误示例
{
"code": 500,
"message": "服务器内部错误",
"data": null
}
为什么要这样写?
1、业务代码极度干净
不需要在每个 if 判断里重复写错误 JSON。
不符合条件 → raise;正常逻辑 → return。
2、全站返回格式强制统一
无论谁写接口,格式永远一致,前端无需适配多种结构。
3、后期维护极其方便
如果后期要改返回字段(比如 message 改成 msg),只改一处,不用改全部接口。
4、隐藏服务器报错,安全性高
代码崩溃、数据库报错不会暴露堆栈信息,生产环境必备。
5、完全贴合国内开发规范
HTTP 状态码永远为 200,前端不用处理 http 异常拦截,只判断 code。
总结
不符合业务条件:raise HTTPException(交给全局处理器)
正常业务逻辑:return success_response()(统一成功格式)
代码意外崩溃:自动走500异常(隐藏错误)
HTTP状态码永远200,code区分业务状态(国内规范)
拓展
后续可以在此基础上升级:
自定义业务异常枚举(1001未登录、1002密码错误)
异常自动日志记录
全局请求拦截、token校验
统一分页返回模板