在API開發中,錯誤處理是確保系統健壯性和用戶體驗的關鍵。當用戶請求出現問題(比如參數錯誤、資源不存在)時,良好的錯誤處理能讓API返回清晰的提示,幫助調用方快速定位問題。FastAPI提供了簡潔高效的錯誤處理機制,讓我們既能靈活使用HTTP狀態碼,又能優雅地捕獲和處理異常。
一、爲什麼需要錯誤處理?¶
想象一下,如果用戶請求一個不存在的用戶ID,而API直接崩潰或返回空白,用戶會非常困惑。正確的錯誤處理可以:
- 向調用方返回明確的錯誤原因(比如“用戶不存在”)
- 使用標準的HTTP狀態碼(如404、400),讓前端或其他系統更容易解析
- 避免程序因未處理的異常而崩潰,保證服務穩定
二、HTTP狀態碼:API的“紅綠燈”¶
HTTP狀態碼是錯誤處理的基礎,FastAPI支持所有標準HTTP狀態碼。常見的狀態碼及使用場景如下:
| 狀態碼 | 含義 | 適用場景 |
|---|---|---|
| 200 | 成功 | 請求正常處理,返回數據 |
| 400 | 參數錯誤 | 請求參數不合法(如格式錯誤) |
| 404 | 資源不存在 | 請求的資源(如用戶、商品)不存在 |
| 401 | 未授權 | 需要登錄但未提供有效憑證 |
| 403 | 禁止訪問 | 權限不足 |
| 422 | 驗證錯誤 | 請求參數格式正確但驗證失敗 |
| 500 | 服務器內部錯誤 | 服務端邏輯出錯(如數據庫連接失敗) |
FastAPI中使用狀態碼:在路由函數中,可通過return或raise直接指定狀態碼。例如:
from fastapi import FastAPI, HTTPException
app = FastAPI()
# 成功響應(默認200)
@app.get("/health")
def health_check():
return {"status": "OK"} # 自動返回200狀態碼
# 明確返回404狀態碼
@app.get("/items/{item_id}")
def get_item(item_id: int):
if item_id not in [1, 2, 3]: # 假設只有1、2、3存在
return {"status": "error", "detail": "Item not found"}, 404 # 直接返回狀態碼
三、FastAPI異常捕獲:主動“拋出”錯誤¶
FastAPI推薦使用HTTPException類主動拋出錯誤,它能直接指定狀態碼和錯誤信息,讓錯誤處理更清晰。
1. 基礎用法:HTTPException¶
from fastapi import HTTPException
@app.get("/users/{user_id}")
def get_user(user_id: int):
# 模擬數據庫查詢
users = {1: "Alice", 2: "Bob"}
if user_id not in users:
# 拋出HTTP異常,指定狀態碼和詳細信息
raise HTTPException(
status_code=404, # 資源不存在
detail=f"用戶ID {user_id} 不存在", # 錯誤描述
headers={"X-Error-Reason": "User not found"} # 可選響應頭
)
return {"user_id": user_id, "name": users[user_id]}
效果:當請求/users/999時,API會返回:
{
"detail": "用戶ID 999 不存在"
}
狀態碼爲404,前端可根據狀態碼判斷“資源不存在”。
2. 參數驗證錯誤:自動返回422¶
FastAPI會自動處理參數格式錯誤(如類型不匹配),並返回422狀態碼。例如:
@app.get("/users/{user_id}")
def get_user(user_id: int): # 強制user_id爲整數
# 如果用戶傳非整數(如字符串),FastAPI自動攔截並返回422
return {"user_id": user_id}
效果:請求/users/abc時,API返回:
{
"detail": [
{
"loc": ["path", "user_id"],
"msg": "輸入的值不是有效的整數",
"type": "type_error.integer"
}
]
}
狀態碼爲422(驗證錯誤),包含具體的錯誤字段和原因。
四、自定義異常:業務邏輯的“專屬錯誤”¶
當業務邏輯出現錯誤(如“餘額不足”“權限不足”)時,可自定義異常類,配合FastAPI的異常處理機制統一管理。
1. 定義自定義異常¶
class InsufficientFundsError(Exception):
"""自定義異常:餘額不足"""
def __init__(self, balance: float, needed: float):
self.balance = balance # 當前餘額
self.needed = needed # 需要的金額
self.detail = f"餘額不足,當前餘額: {balance},需要: {needed}"
2. 全局異常處理¶
通過@app.exception_handler註冊自定義異常的處理函數,實現全局統一處理:
from fastapi import Request, status
from fastapi.responses import JSONResponse
# 處理InsufficientFundsError異常
@app.exception_handler(InsufficientFundsError)
async def handle_insufficient_funds(request: Request, exc: InsufficientFundsError):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST, # 400狀態碼
content={"detail": exc.detail} # 返回自定義錯誤信息
)
3. 在路由中使用自定義異常¶
@app.post("/withdraw")
def withdraw(amount: float):
balance = 100.0 # 假設當前餘額
if amount > balance:
# 拋出自定義異常
raise InsufficientFundsError(balance=balance, needed=amount)
return {"message": "轉賬成功", "balance": balance - amount}
效果:請求/withdraw?amount=200時,API返回:
{
"detail": "餘額不足,當前餘額: 100.0,需要: 200.0"
}
五、全局錯誤處理:統一“兜底”策略¶
當出現未預料到的異常(如數據庫連接失敗)時,全局異常處理能避免重複代碼,統一返回標準錯誤。
from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
# 處理所有未捕獲的異常(通用兜底)
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
# 記錄錯誤日誌(生產環境建議使用logging)
print(f"未處理異常: {str(exc)}")
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={"detail": "服務器內部錯誤,請聯繫管理員"}
)
六、最佳實踐總結¶
- 用HTTPException處理HTTP標準錯誤:如404(資源不存在)、400(參數錯誤)、422(驗證失敗),直接拋出並指定狀態碼。
- 用自定義異常處理業務邏輯錯誤:如“餘額不足”“權限不足”,配合
exception_handler統一返回。 - 自動參數驗證:FastAPI會自動處理類型錯誤,返回422狀態碼,無需手動捕獲。
- 全局異常處理:通過
@app.exception_handler統一兜底未捕獲異常,避免重複代碼。
示例代碼整合¶
from fastapi import FastAPI, HTTPException, Request, status
from fastapi.responses import JSONResponse
app = FastAPI()
# 模擬用戶數據
users = {1: "Alice", 2: "Bob"}
# 自定義異常
class InsufficientFundsError(Exception):
def __init__(self, balance: float, needed: float):
self.balance = balance
self.needed = needed
self.detail = f"餘額不足,當前餘額: {balance},需要: {needed}"
# 全局異常處理:兜底所有未捕獲異常
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
print(f"全局錯誤: {str(exc)}")
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={"detail": "服務器內部錯誤"}
)
# 處理自定義異常:餘額不足
@app.exception_handler(InsufficientFundsError)
async def handle_insufficient_funds(request: Request, exc: InsufficientFundsError):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content={"detail": exc.detail}
)
# 1. 獲取用戶(處理404)
@app.get("/users/{user_id}")
def get_user(user_id: int):
if user_id not in users:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"用戶ID {user_id} 不存在"
)
return {"user_id": user_id, "name": users[user_id]}
# 2. 模擬轉賬(處理自定義異常)
@app.post("/withdraw")
def withdraw(amount: float):
balance = 100.0
if amount > balance:
raise InsufficientFundsError(balance=balance, needed=amount)
return {"message": "轉賬成功", "remaining": balance - amount}
通過以上內容,你已經掌握了FastAPI錯誤處理的核心方法:用HTTP狀態碼規範錯誤類型,用HTTPException和自定義異常處理具體錯誤場景,並通過全局異常處理統一兜底。這些技巧能幫助你構建更健壯、友好的API服務。