本文 AI 產出,尚未審核

Python 課程 – 網路與 API(Networking & APIs)

主題:FastAPI / Flask 應用


簡介

在現代的 Web 開發與微服務架構中,API(Application Programming Interface) 已成為前後端、服務與服務之間溝通的核心橋樑。Python 以其簡潔易讀、套件豐富的特性,成為開發 RESTful API 的熱門選擇,而 FastAPIFlask 則是兩個最常被採用的框架。

  • Flask:自 2010 年問世,以「微框架」的概念提供最小化的核心,開發者可以自行挑選套件來擴充功能,彈性極高。
  • FastAPI:自 2018 年推出,以 型別註解(type hints) 為基礎,結合 StarlettePydantic,在 效能(接近 Node.js、Go)與 自動文件產生(OpenAPI/Swagger)方面表現卓越。

本篇文章將從概念、實作、常見陷阱與最佳實踐,帶領讀者一步步掌握這兩個框架的基礎與進階應用,讓你能快速上手、開發出可維護、效能佳的 API 服務。


核心概念

1️⃣ 為什麼要使用框架?

  • 路由管理:將 URL 與對應的處理函式(view)對應起來,讓程式結構更清晰。
  • 請求與回應抽象:框架會把 HTTP 請求封裝成物件,提供便利的方法取得 query、body、header 等資訊。
  • 中介層(Middleware):支援跨域、認證、日誌等共通功能,減少重複程式碼。
  • 自動文件:特別是 FastAPI,會根據型別註解自動產生 Swagger UI 與 ReDoc,降低前後端溝通成本。

2️⃣ Flask 基礎

2.1 安裝與建立第一個應用

pip install flask
# app.py
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route("/")
def hello():
    """根路由,回傳簡單訊息"""
    return "Hello, Flask!"

if __name__ == "__main__":
    # 啟動開發伺服器,debug=True 會自動 reload
    app.run(host="0.0.0.0", port=5000, debug=True)

註解@app.route("/") 為路由裝飾器,將根 URL (/) 對應到 hello 函式。jsonify 用於把 Python 資料結構自動轉成 JSON。

2.2 取得 Query 參數與 JSON Body

@app.route("/greet", methods=["GET"])
def greet():
    # 取得 ?name=xxx 的 query 參數,若未提供則預設為 "World"
    name = request.args.get("name", "World")
    return jsonify({"message": f"Hello, {name}!"})

@app.route("/echo", methods=["POST"])
def echo():
    # request.get_json() 會把 JSON 解析成 dict
    data = request.get_json()
    # 回傳相同的資料,示範 POST 接收
    return jsonify({"you_sent": data}), 201

2.3 使用 Blueprint 進行模組化

# user_routes.py
from flask import Blueprint, jsonify

user_bp = Blueprint("user", __name__, url_prefix="/users")

@user_bp.route("/", methods=["GET"])
def list_users():
    # 假資料
    users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
    return jsonify(users)

# 在主程式中註冊
app.register_blueprint(user_bp)

重點:Blueprint 讓你把相關路由分散到不同檔案,提升專案可維護性。


3️⃣ FastAPI 基礎

3.1 安裝與建立第一個應用

pip install fastapi uvicorn
# main.py
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="示範 FastAPI", version="0.1.0")

@app.get("/")
async def root():
    """根路由,回傳文字"""
    return {"message": "Hello, FastAPI!"}

執行:

uvicorn main:app --reload

說明uvicorn 是 ASGI 伺服器,--reload 讓程式碼變更自動重新載入,類似 Flask 的 debug=True

3.2 型別註解與自動驗證

class Item(BaseModel):
    name: str
    price: float
    tags: list[str] = []   # 預設為空陣列

@app.post("/items/")
async def create_item(item: Item):
    """
    接收 JSON 並自動驗證:
    - name 必須是字串
    - price 必須是正浮點數
    - tags 為字串陣列(可省略)
    """
    return {"item_id": 1, "item": item}

特點:若傳入的 JSON 不符合 Item 的結構,FastAPI 會自動回傳 422 錯誤,並在 Swagger UI 中顯示錯誤訊息。

3.3 路徑參數、查詢參數與 Header

from fastapi import Path, Query, Header

@app.get("/users/{user_id}")
async def read_user(
    user_id: int = Path(..., title="使用者 ID", ge=1),
    q: str | None = Query(None, max_length=50),
    x_token: str = Header(..., alias="X-Token")
):
    """
    - Path 參數會自動轉型為 int,且必須大於等於 1。
    - Query 參數是可選的,若提供則長度不可超過 50。
    - Header 參數使用別名 X-Token,必須提供。
    """
    return {"user_id": user_id, "q": q, "token": x_token}

3.4 中介層(Middleware)與例外處理

from fastapi.middleware.cors import CORSMiddleware
from fastapi import HTTPException, Request

# 允許所有來源的 CORS(開發階段常用)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    # 自訂錯誤回傳格式
    return JSONResponse(
        status_code=exc.status_code,
        content={"detail": exc.detail, "path": request.url.path},
    )

3.5 自動產生 API 文件

  • 開啟 http://127.0.0.1:8000/docs 可看到 Swagger UI
  • 開啟 http://127.0.0.1:8000/redoc 可看到 ReDoc

只要寫好型別註解,文件會即時更新,對前端或第三方開發者非常友善。


常見陷阱與最佳實踐

陷阱 可能的後果 解決方案 / 最佳實踐
同步阻塞 I/O(如直接呼叫 time.sleep 會卡住整個事件迴圈,導致所有請求變慢。 使用 await asyncio.sleep 或把阻塞任務交給執行緒池 (run_in_threadpool)。
未設定 CORS 前端跨域請求被瀏覽器阻擋,無法存取 API。 在 FastAPI 加入 CORSMiddleware,或在 Flask 使用 flask-cors
路由衝突 同一路徑被多個裝飾器註冊,導致 404 或不預期行為。 統一規劃路由前綴,使用 Blueprint(Flask)或 APIRouter(FastAPI)分割模組。
缺乏輸入驗證 會收到不合法資料,引發例外或資料庫錯誤。 依賴 Pydantic(FastAPI)或手動驗證(Flask‑Marshmallow)。
過度依賴全域變數 多執行緒/多程序環境下會產生 race condition。 使用依賴注入(FastAPI 的 Depends)或 Flask 的 app.configg 物件。
未設置日誌 難以追蹤錯誤與效能瓶頸。 結合 logurustructlog 或 Python 標準 logging,在 Middleware 中紀錄請求/回應。
開發環境直接使用生產資料庫 資料遺失或測試失誤。 使用環境變數切換 SQLALCHEMY_DATABASE_URI / DATABASE_URL,或使用 Docker Compose 分離。

最佳實踐

  1. 型別註解 + Pydantic:讓資料驗證與序列化自動化,減少手寫驗證程式。
  2. 分層架構router → service → repository,保持業務邏輯與資料存取分離。
  3. 依賴注入:FastAPI 的 Depends 可讓測試變得更簡單,Flask 則可透過 Flask‑Injector。
  4. 單元測試:使用 pytest + httpx(FastAPI)或 FlaskClient(Flask)寫測試,確保路由、驗證、錯誤處理都正確。
  5. 容器化部署:撰寫 Dockerfile,搭配 uvicorn --host 0.0.0.0gunicorn -w 4 -k uvicorn.workers.UvicornWorker,提升可移植性與可擴展性。

實際應用場景

場景 適合框架 為什麼選擇
快速原型、單一服務 Flask 輕量、上手快,對於簡單 CRUD 足夠。
高併發微服務、需要自動文件 FastAPI 非同步支援、效能佳、內建 OpenAPI。
需要與舊有大型 Flask 專案共存 Flask + Flask‑RESTful 可逐步遷移至 FastAPI,保持相容性。
資料科學模型部署(如機器學習) FastAPI async 處理大量請求,同時支援 pydantic 直接映射模型輸入。
IoT 裝置或 Edge 計算 FastAPI(使用 uvicorn + --workers 1 輕量且支援 WebSocket,適合即時資料傳輸。
需要自訂認證與授權流程 FastAPI(OAuth2、JWT) 內建 Security 模組,簡易實作 OAuth2 密碼流程。

範例:將一個機器學習模型(例如 scikit‑learn)以 FastAPI 包裝,提供 /predict 端點,接受 JSON 輸入並回傳預測結果。此情境下,FastAPI 的非同步特性可同時處理多個預測請求,且自動產生的 Swagger 文件讓資料科學家直接測試 API。


總結

  • Flask 以極高的彈性與簡潔的 API 讓開發者能快速上手,適合小型服務或需要自行挑選套件的情境。
  • FastAPI 則以 型別註解 + 非同步 為核心,提供近乎即時的效能與自動化文件,是構建 高併發、可維護的 RESTful API 的首選。
  • 了解 路由、請求/回應、驗證、錯誤處理、Middleware 等共通概念後,兩者的差異僅在於 開發體驗與效能需求
  • 掌握 最佳實踐(分層架構、依賴注入、容器化、日誌與測試)可讓你的 API 從原型階段平順過渡到正式上線、可擴展的生產環境。

下一步:挑選一個小專案(例如 Todo List API),先用 Flask 完成最基本的 CRUD,之後將其改寫為 FastAPI,體驗兩者在 開發速度、效能、文件產生 方面的差異。透過實作,你將能更自信地在不同情境下選擇最合適的框架,為未來的微服務或大型系統奠定堅實基礎。