我們在上一章節已經處理好了資料庫的相關設定與資料表遷移。接下來要開始進入API端點的建立環節。在建立端點前,我們先了解說如果使用者對端點發出請求後如果處理程序之中發生了問題,我們該怎麼解決,怎麼跟使用者告知錯誤。
狀態碼表示一個 HTTP 請求是否已經被完成,並各自表示不同的狀態。我們最耳熟能詳的當然就是 404 Not Found 這個狀態碼,表示說伺服器找不到該資源,在網路世界中十分常被使用。
更多詳細定義請參照以下連結:
HTTP 狀態碼 - HTTP | MDN (mozilla.org)
我們透過使用 FastAPI 中的 HTTPException 類別,便可以對用戶端返回HTTP錯誤回應。
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:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
可以看到我們在驗證物品失敗後 raise HTTPException類別便可以對用戶端返回指定狀態碼和訊息。
如果我們有相同的錯誤在不同的端點中發生,我們就需要每次都輸入狀態碼跟錯誤訊息,會有點太壟長,我們如果使用自訂的類別,就可以在不同的端點中複用。
from fastapi import HTTPException, status
from typing import Any
class DetailedHTTPException(HTTPException):
STATUS_CODE = status.HTTP_500_INTERNAL_SERVER_ERROR
DETAIL = "Server error"
def __init__(self, **kwargs: dict[str, Any]) -> None:
super().__init__(status_code=self.STATUS_CODE, detail=self.DETAIL, **kwargs)
我們自訂一個 DetailedHTTPException 類別繼承原本的HTTPException,並在實體化的時候去繼承原本的 init 方法。這樣子就可以只修改自訂類別中的 STATUS_CODE 、DETAIL 來回傳不同的狀態情境。
class RequestTimeout(DetailedHTTPException):
STATUS_CODE = status.HTTP_504_GATEWAY_TIMEOUT
DETAIL = "Request timeout"
例如我們新增一個 RequestTimeout 類別來繼承 DetailedHTTPException ,就可以修改裡面的 STATUS_CODE 、DETAIL 來回傳 timeout 的情境,以在每次 timeout 的時候只需要 raise 這個 Exception 就可以了。
class BadRequest(DetailedHTTPException):
STATUS_CODE = status.HTTP_400_BAD_REQUEST
DETAIL = "Bad Request"
class NotAuthenticated(DetailedHTTPException):
STATUS_CODE = status.HTTP_401_UNAUTHORIZED
DETAIL = "User not authenticated"
def __init__(self) -> None:
super().__init__(headers={"WWW-Authenticate": "Bearer"})
class PermissionDenied(DetailedHTTPException):
STATUS_CODE = status.HTTP_403_FORBIDDEN
DETAIL = "Permission denied"
class NotFound(DetailedHTTPException):
STATUS_CODE = status.HTTP_404_NOT_FOUND
class ServerError(DetailedHTTPException):
STATUS_CODE = status.HTTP_500_INTERNAL_SERVER_ERROR
def __init__(self, detail: str) -> None:
self.DETAIL = detail
super().__init__()
以上類別為常用的一些 Exception,我們可以預先定義好這些類別,在各個模組中可以在新增 Exceptions 來繼承這些類別,再修改 DETAIL 便可以來去符合各種情境。
我們下一篇開始新增端點操作的時候,我們將會透過剛剛那些自定義的 Exceptions 來去設計我們的程式碼要在什麼時機點給予用戶端什麼樣的回應。這樣用戶端也可以根據不同的回應來給予使用者對應的提示,才會是一個合格的應用程式。