FastAPI Response 錯誤處理 (Handling Errors)
在 Web API 開發中,正確地處理錯誤並回傳適當的 HTTP 狀態碼給客戶端是非常重要的。例如:
- 找不到資源 -> 404 Not Found
- 權限不足 -> 401 Unauthorized / 403 Forbidden
- 輸入錯誤 -> 400 Bad Request
FastAPI 提供了 HTTPException 來讓你輕鬆處理這些情況。
使用 HTTPException
你需要從 fastapi 導入 HTTPException。
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
# 當找不到 item 時,拋出 HTTPException
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
當你存取 /items/bar (不存在) 時,你會收到:
HTTP Status Code: 404 Not Found
Response Body:
{
"detail": "Item not found"
}
為什麼是 raise 而不是 return?
因為 HTTPException 是一個 Python 的 例外 (Exception)。
當你 raise 它時:
- 原本的函數執行會立刻中斷。
- FastAPI 的例外處理器會捕捉到它,並將其轉換為 HTTP 回應。
- 這也意味著你不必在每個
if-else分支都加上 return,程式碼會更簡潔。
自訂 Response Header
有時候你需要在回傳錯誤時加上特定的 Header (例如安全性相關 header),可以在 HTTPException 中加入 headers 參數。
raise HTTPException(
status_code=401,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
自訂例外處理器 (Exception Handlers)
如果你希望統一處理某種自定義例外,可以註冊 Exception Handler。
假設你有一個自訂例外 UnicornException:
class UnicornException(Exception):
def __init__(self, name: str):
self.name = name
你可以使用 @app.exception_handler 裝飾器來定義如何處理它:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
)
@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}
當請求 /unicorns/yolo 時,你會得到 418 I'm a teapot 狀態碼與自訂訊息。
覆寫預設的驗證錯誤 (RequestValidationValidationError)
當 Pydantic 驗證失敗時,FastAPI 預設會回傳 422 狀態碼和詳細的 JSON (包含 loc, msg, type)。如果你想改變這個預設行為,可以覆寫 RequestValidationError 的處理器。
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return PlainTextResponse(str(exc), status_code=400)
這樣驗證錯誤時就不會回傳 JSON,而是純文字,狀態碼也變成 400。
覆寫 HTTPExceptions
你甚至可以覆寫 FastAPI 內建的 HTTPException 處理器(也就是處理你上面 raise HTTPException 的那個)。
例如,你希望所有 API 的錯誤回應格式都是 {"error": "message"} 而不是 {"detail": "message"}:
from starlette.exceptions import HTTPException as StarletteHTTPException
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={"error": exc.detail}, # 將 key 從 detail 改為 error
)
starlette.exceptions.HTTPException,因為 FastAPI 的 HTTPException 繼承自它,而 Exception Handler 實際上是在處理 Starlette 層級的例外。總結
- 使用
raise HTTPException(status_code=..., detail=...)來中斷執行並回傳錯誤。 - 使用
@app.exception_handler來定義特定例外的全域處理邏輯。 - 這讓你能夠統一管理錯誤回應的格式,保持 API 風格一致。