FastAPI 實(shí)戰(zhàn)技巧:統(tǒng)一響應(yīng)格式 + 全局異常處理,一次搞定!
在開發(fā) FastAPI 項(xiàng)目的過程中,你是否遇到過這些問題?
- 每個(gè)接口的返回結(jié)構(gòu)不統(tǒng)一,前端不好處理?
- 拋出異常時(shí)直接報(bào) 500,連自己都看不懂?
- 想統(tǒng)一日志、錯(cuò)誤、結(jié)構(gòu)處理,結(jié)果寫得四分五裂?
這篇文章帶你一步步落地:統(tǒng)一響應(yīng)結(jié)構(gòu) + 全局異常處理機(jī)制,讓接口更規(guī)范、更易維護(hù)!
1. 為什么要統(tǒng)一響應(yīng)格式?
統(tǒng)一返回格式的好處:
- 前端開發(fā)更容易解析數(shù)據(jù)結(jié)構(gòu)
- 后期維護(hù)更省心,不用滿項(xiàng)目找問題
- 可擴(kuò)展性強(qiáng),適配多種客戶端需求
推薦標(biāo)準(zhǔn)格式如下:
{
"code":0,
"message":"OK",
"data":{
"id":1,
"name":"Alice"
}
}
字段解釋:
字段名 | 類型 | 說明 |
code | int | 自定義業(yè)務(wù)狀態(tài)碼 |
message | str | 提示信息 |
data | object | 實(shí)際返回的業(yè)務(wù)數(shù)據(jù),支持對象/數(shù)組/null |
2. 響應(yīng)模型封裝
定義通用響應(yīng)模型(支持泛型):
from pydantic.generics import GenericModel
from typing import Generic, TypeVar, Optional
T = TypeVar("T")
class Response(GenericModel, Generic[T]):
code: int = 0
message: str = "OK"
data: Optional[T] = None
這種結(jié)構(gòu)支持響應(yīng)任何類型的數(shù)據(jù),只需:
@app.get("/user/{user_id}", response_model=Response[UserOut])
async def get_user(user_id: int):
user = await User.get(id=user_id)
return Response(data=user)
接口文檔中將展示完整的結(jié)構(gòu),同時(shí)前端調(diào)用也能穩(wěn)定解析。
3. 全局異常處理
FastAPI 默認(rèn)錯(cuò)誤返回 HTTP 500 和系統(tǒng) Traceback,不適合直接暴露給客戶端。
我們可以自定義異常類 + 注冊全局處理器。
(1) 自定義業(yè)務(wù)異常
class BusinessException(Exception):
def __init__(self, code: int = 4001, message: str = "業(yè)務(wù)異常"):
self.code = code
self.message = message
(2) 注冊異常處理器
from fastapi.responses import JSONResponse
from fastapi import Request, FastAPI
defregister_exceptions(app: FastAPI):
@app.exception_handler(BusinessException)
asyncdefbusiness_exception_handler(request: Request, exc: BusinessException):
return JSONResponse(
status_code=200,
content={
"code": exc.code,
"message": exc.message,
"data": None
}
)
@app.exception_handler(Exception)
asyncdefgeneral_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={
"code": 5000,
"message": "系統(tǒng)異常",
"data": None
}
)
調(diào)用:
register_exceptions(app)
4. 異常使用示例
@app.get("/users/{user_id}", response_model=Response[UserOut])
async def get_user(user_id: int):
user = await User.get_or_none(id=user_id)
if not user:
raise BusinessException(code=4040, message="用戶不存在")
return Response(data=user)
5. 項(xiàng)目推薦結(jié)構(gòu)
app/
├── main.py # 啟動(dòng)入口
├── api/
│ └── user.py # 路由模塊
├── core/
│ ├── response.py # 響應(yīng)模型
│ ├── exception.py # 異常類與注冊器
(1) response.py
from pydantic.generics import GenericModel
from typing import Generic, TypeVar, Optional
T = TypeVar("T")
class Response(GenericModel, Generic[T]):
code: int = 0
message: str = "OK"
data: Optional[T] = None
(2) exception.py
from fastapi import Request, FastAPI
from fastapi.responses import JSONResponse
classBusinessException(Exception):
def__init__(self, code: int = 4001, message: str = "業(yè)務(wù)異常"):
self.code = code
self.message = message
defregister_exceptions(app: FastAPI):
@app.exception_handler(BusinessException)
asyncdefbusiness_handler(request: Request, exc: BusinessException):
return JSONResponse(
status_code=200,
content={"code": exc.code, "message": exc.message, "data": None}
)
@app.exception_handler(Exception)
asyncdefglobal_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"code": 5000, "message": "系統(tǒng)錯(cuò)誤", "data": None}
)
(3) main.py
from fastapi import FastAPI
from app.core.exception import register_exceptions
from app.api import user
app = FastAPI()
register_exceptions(app)
app.include_router(user.router)
6. 總結(jié)
- 統(tǒng)一響應(yīng)結(jié)構(gòu) 提高接口一致性,利于前后端協(xié)作
- 異常統(tǒng)一處理 避免信息泄露、增強(qiáng)健壯性
- 泛型封裝響應(yīng)模型,優(yōu)雅又實(shí)用!