FastAPI 框架以其高效能和易用性受到廣泛關注,對於建構大型應用程式,有效管理程式碼結構變得至關重要。本文介紹如何運用 APIRouter 模組化 API 路由,將相關 API 端點組織成獨立模組,提升程式碼可讀性和可維護性。同時,探討子應用程式的掛載方式,讓不同功能模組獨立開發和佈署,並說明如何使用依賴注入機制簡化程式碼邏輯,提升程式碼的重用性和可測試性。最後,介紹 FastAPI 中介層的應用,例如 CORS 設定、請求驗證等,以及如何撰寫自定義中介層,強化應用程式的功能和安全性。

更大的應用程式架構設計

在開發複雜的Web應用程式時,如何組織程式碼結構以保持可維護性和擴充套件性是一項重要的挑戰。本章節將探討如何使用FastAPI框架來構建更大規模的應用程式,包括使用APIRouter來模組化路由、使用子應用程式來組織獨立的功能模組,以及如何使用依賴注入來簡化程式碼邏輯。

使用APIRouter進行模組化路由

在FastAPI中,APIRouter是一種用於組織和模組化路由的機制。它允許開發者將相關的路由分組到不同的模組中,從而提高程式碼的可讀性和可維護性。

建立APIRouter例項

首先,我們需要在不同的模組中建立APIRouter例項。例如,在一個名為albums的模組中,我們可以建立一個專門處理相簿相關操作的APIRouter

# albums/albums.py
from fastapi import APIRouter
from .models import Album

albums_router = APIRouter(prefix="/albums", tags=["albums"])

# 路由定義
@albums_router.get("/")
async def get_albums():
    return {"message": "取得相簿列表"}

將APIRouter例項註冊到主應用程式

接下來,我們需要在主應用程式中匯入並註冊這些APIRouter例項:

# main.py
from fastapi import FastAPI
from albums import albums_router
from books import books_router

app = FastAPI()

app.include_router(albums_router)
app.include_router(books_router)

@app.get("/")
async def root():
    return {"message": "首頁"}

內容解密:

  1. 模組化路由:透過使用APIRouter,我們可以將不同的路由分組到不同的模組中,使得程式碼結構更加清晰。
  2. 字首和標籤:在建立APIRouter例項時,可以指定字首和標籤,這有助於自動生成API檔案。
  3. 註冊路由:透過include_router方法將APIRouter例項註冊到主應用程式中。

掛載子應用程式

除了使用APIRouter進行模組化路由外,FastAPI還支援掛載子應用程式。這使得我們可以建立完全獨立的子應用程式,並將它們掛載到主應用程式上。

建立子應用程式

首先,我們需要建立獨立的子應用程式。例如,建立一個處理相簿相關操作的子應用程式:

# albums/albums.py
from fastapi import FastAPI

albums_app = FastAPI()

@albums_app.get("/albums")
async def get_albums():
    return {"message": "取得相簿列表"}

掛載子應用程式到主應用程式

然後,在主應用程式中匯入並掛載這些子應用程式:

# main.py
from fastapi import FastAPI
from albums import albums_app
from books import books_app

app = FastAPI()

app.mount("/albumapi", albums_app)
app.mount("/bookapi", books_app)

@app.get("/stores")
async def root():
    return {"message": "首頁"}

內容解密:

  1. 獨立子應用程式:每個子應用程式都是完全獨立的FastAPI例項,可以擁有自己的路由和組態。
  2. 掛載子應用程式:透過mount方法將子應用程式掛載到主應用程式的特定路徑下。
  3. 獨立檔案:每個子應用程式都有自己的API檔案,可以透過掛載路徑下的/docs存取。

依賴注入

依賴注入是一種設計模式,用於管理函式或類別之間的依賴關係。在FastAPI中,依賴注入是透過Depends函式實作的。

簡單的依賴注入示例

假設我們有一個需求,需要在每個路徑操作函式中檢查當前是否是星期日。我們可以定義一個函式來檢查當前日期,並將其作為依賴注入到路徑操作函式中:

# dependencies.py
from datetime import datetime

def is_sunday():
    today = datetime.today().weekday()
    return today == 6

# main.py
from fastapi import FastAPI, Depends

app = FastAPI()

@app.get("/items/")
async def read_items(is_sunday: bool = Depends(is_sunday)):
    if is_sunday:
        return {"message": "今天是星期日,服務暫停"}
    else:
        return {"message": "服務正常執行"}

內容解密:

  1. 定義依賴:首先定義一個函式作為依賴,這個函式傳回一個值。
  2. 注入依賴:在路徑操作函式中使用Depends將依賴函式注入,並取得其傳回值。
  3. 使用依賴:根據依賴的傳回值進行相應的邏輯處理。

深入解析FastAPI中的依賴注入機制

FastAPI的依賴注入系統是其強大功能之一,能夠有效管理程式碼的耦合度並提升可維護性。本章節將詳細探討如何在FastAPI中使用依賴注入來簡化程式碼並增強應用程式的靈活性。

基礎依賴函式的使用

在FastAPI中,依賴函式可以用於執行特定的任務,例如驗證、資料擷取或其他需要重複使用的邏輯。以下是一個簡單的依賴函式範例,用於判斷當前星期幾:

from datetime import datetime
from fastapi import Depends, FastAPI

app = FastAPI()

def get_current_weekday():
    """取得當前星期幾"""
    return datetime.now().weekday()

@app.get("/")
async def root(day: int = Depends(get_current_weekday)):
    """根路由,根據星期幾傳回不同訊息"""
    if day == 6:
        return {"message": "週日服務暫停"}
    return {"message": "首頁"}

內容解密:

  1. get_current_weekday函式用於取得當前日期的星期幾(0代表星期一,6代表星期日)。
  2. root路由中使用Dependsget_current_weekday作為依賴注入。
  3. 根據傳回的星期幾值決定傳回的訊息內容。

查詢引數作為依賴

依賴函式可以是普通的函式或使用async def定義的協程函式。以下範例展示瞭如何使用協程函式作為依賴來處理查詢引數:

from fastapi import Depends, FastAPI

app = FastAPI()

async def get_range_params(x: int, y: int):
    """取得範圍引數"""
    return {"from": x, "to": y}

@app.get("/persons/")
async def get_persons(params: dict = Depends(get_range_params)):
    """取得人員列表的子集"""
    persons = [
        {"name": "Tom", "age": 20},
        {"name": "Mark", "age": 25},
        {"name": "Pam", "age": 27}
    ]
    return persons[params['from']:params['to']]

內容解密:

  1. get_range_params是一個協程函式,用於處理查詢引數xy
  2. /persons/路由中使用Depends注入get_range_params
  3. 傳回的人員列表會根據查詢引數進行切片處理。

使用類別作為依賴

除了函式外,FastAPI也支援使用類別作為依賴。以下範例展示瞭如何將前面的get_range_params函式轉換為一個類別:

from fastapi import Depends, FastAPI

app = FastAPI()

class RangeParams:
    def __init__(self, x: int, y: int):
        self.x = x
        self.y = y

@app.get("/persons/")
async def get_persons(params: RangeParams = Depends(RangeParams)):
    """使用類別依賴取得人員列表"""
    persons = [
        {"name": "Tom", "age": 20},
        {"name": "Mark", "age": 25},
        {"name": "Pam", "age": 27}
    ]
    return persons[params.x:params.y]

內容解密:

  1. 定義了一個RangeParams類別,其建構函式接受xy兩個引數。
  2. 在路由中使用Depends(RangeParams)來注入該類別的例項。
  3. 使用類別例項的屬性來進行列表切片。

可呼叫類別作為依賴

Python中的類別可以透過實作__call__方法變成可呼叫的物件。以下範例展示瞭如何將類別轉換為可呼叫的依賴:

class CallableRangeParams:
    def __call__(self, x: int, y: int):
        self.x = x
        self.y = y
        return self

@app.get("/persons/")
async def get_persons(params: CallableRangeParams = Depends(CallableRangeParams())):
    """使用可呼叫類別依賴"""
    persons = [
        {"name": "Tom", "age": 20},
        {"name": "Mark", "age": 25},
        {"name": "Pam", "age": 27}
    ]
    return persons[params.x:params.y]

內容解密:

  1. 定義了一個CallableRangeParams類別,並實作了__call__方法。
  2. 使用該類別的例項作為依賴注入到路由中。
  3. 路由函式中使用該例項的屬性來處理資料。

帶引數的依賴函式

依賴函式本身也可以接受引數,這使得它們更加靈活。以下範例展示瞭如何使用帶引數的依賴函式進行驗證:

from fastapi import FastAPI, Request, Depends
from fastapi.responses import Response

app = FastAPI()

def verify_api_key(request: Request):
    """驗證API金鑰"""
    try:
        return request.cookies.get('api_key')
    except Exception:
        return None

@app.get("/")
async def index(request: Request, response: Response):
    """設定API金鑰cookie"""
    response.set_cookie(key="api_key", value="abcdef12345")
    return {"message": "首頁"}

@app.get("/protected/")
async def protected_route(api_key: str = Depends(verify_api_key)):
    """受保護的路由"""
    if api_key is None:
        return {"message": "未驗證的API金鑰"}
    return {"message": "驗證成功"}

內容解密:

  1. verify_api_key函式用於驗證請求中的API金鑰。
  2. /protected/路由中使用該函式作為依賴進行驗證。
  3. 如果驗證失敗,則傳回錯誤訊息。

最佳實踐建議

  1. 適當使用非同步依賴:對於涉及I/O操作的依賴,建議使用非同步函式以提高效能。
  2. 保持依賴功能的單一性:每個依賴應該只負責一個特定的功能,以提高可重用性和可測試性。
  3. 妥善處理依賴中的錯誤:在依賴函式中適當地處理可能發生的錯誤,並傳回有意義的錯誤訊息。
  4. 充分利用FastAPI的檔案自動生成特性:適當地為依賴函式和引數新增檔案註解,可以自動生成清晰的API檔案。

透過遵循這些最佳實踐,可以更好地利用FastAPI的依賴注入系統,開發出高效、可靠且易於維護的Web應用程式。

使用 FastAPI 建構大型應用程式:依賴注入與中介層

在開發複雜的 Web 應用程式時,如何有效地管理依賴關係和處理 HTTP 請求是一個重要的課題。FastAPI 提供了多種強大的工具來簡化這些任務,包括依賴注入系統和中介層機制。本篇文章將探討這些主題,並提供實際的程式碼範例來說明其用法。

依賴注入(Dependency Injection)

依賴注入是一種設計模式,用於管理元件之間的依賴關係。在 FastAPI 中,依賴注入系統允許開發者以宣告式的方式定義路徑操作函式所需的依賴項。這些依賴項可以是資料函式庫連線、外部服務的客戶端,或是其他任何需要的資源。

依賴注入的基本用法

from fastapi import Depends, FastAPI

app = FastAPI()

def get_query(q: str = None):
    return q

@app.get("/items/")
async def read_items(q: str = Depends(get_query)):
    return {"q": q}

資料函式庫會話依賴

在處理資料函式庫操作時,經常需要使用資料函式庫會話。FastAPI 的依賴注入系統可以很好地與資料函式庫會話管理結合。

from sqlalchemy.orm import Session

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users/")
def read_users(db: Session = Depends(get_db)):
    users = db.query(models.User).all()
    return users

內容解密:

在上述範例中,get_db 函式負責建立和關閉資料函式庫會話。它使用 yield 陳述式傳回資料函式庫會話物件,而不是直接傳回。這使得 FastAPI 可以在路徑操作函式執行完畢後自動關閉會話,無論操作是否成功。

在裝飾器中使用依賴注入

有時,依賴項不需要被注入到路徑操作函式中,但仍然需要在執行路徑操作之前被解析。這可以透過在路徑裝飾器中指定 dependencies 引數來實作。

from fastapi import Depends, FastAPI, Header, HTTPException

async def verify_header(X_Web_Framework: str = Header()):
    if X_Web_Framework != "FastAPI":
        raise HTTPException(status_code=400, detail="Invalid Header")

app = FastAPI()

@app.get("/items/", dependencies=[Depends(verify_header)])
async def read_items():
    return [{"item": "Item Foo"}]

內容解密:

在這個範例中,verify_header 函式檢查請求頭中是否包含正確的 X-Web-Framework 值。如果檢查失敗,它會引發一個 HTTP 異常。這個依賴項被新增到路徑裝飾器中,確保在執行 read_items 函式之前進行檢查。

中介層(Middleware)

中介層是一種特殊的函式,它攔截每個 HTTP 請求,並在將請求傳遞給相應的路徑操作函式之前或之後執行某些操作。中介層可以用於日誌記錄、身份驗證、修改請求或回應等。

自定義中介層

from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_header(request: Request, call_next):
    print("Middleware executed before path operation function")
    response = await call_next(request)
    response.headers["X-Framework"] = "FastAPI"
    return response

@app.get("/")
async def index():
    return {"message": "Hello World"}

內容解密:

這個範例中的 add_header 中介層函式列印一條訊息到控制檯,然後將請求傳遞給下一個處理函式(即路徑操作函式)。在收到回應後,它新增一個自定義的回應頭 X-Framework

CORS(跨源資源分享)

CORS 是一種安全機制,用於控制 Web 頁面如何與不同源的伺服器進行互動。FastAPI 提供了內建的 CORS 中介層來簡化 CORS 組態。

使用 CORSMiddleware

from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "http://localhost",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

內容解密:

在這個範例中,我們組態了 CORSMiddleware 以允許來自 http://localhost 的請求。allow_credentialsallow_methodsallow_headers 等引數用於進一步自定義 CORS 行為。

隨著 Web 開發技術的不斷進步,FastAPI 也在持續演進。未來,我們可能會看到更多新特性和改進,以進一步簡化開發流程和提高應用效能。同時,隨著更多開發者和團隊採用 FastAPI,我們可以預期看到一個更加活躍和豐富的生態系統,包括更多的第三方函式庫、工具和資源。