Python 課程 – 網路與 API(Networking & APIs)
主題:FastAPI / Flask 應用
簡介
在現代的 Web 開發與微服務架構中,API(Application Programming Interface) 已成為前後端、服務與服務之間溝通的核心橋樑。Python 以其簡潔易讀、套件豐富的特性,成為開發 RESTful API 的熱門選擇,而 FastAPI 與 Flask 則是兩個最常被採用的框架。
- Flask:自 2010 年問世,以「微框架」的概念提供最小化的核心,開發者可以自行挑選套件來擴充功能,彈性極高。
- FastAPI:自 2018 年推出,以 型別註解(type hints) 為基礎,結合 Starlette 與 Pydantic,在 效能(接近 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.config、g 物件。 |
| 未設置日誌 | 難以追蹤錯誤與效能瓶頸。 | 結合 loguru、structlog 或 Python 標準 logging,在 Middleware 中紀錄請求/回應。 |
| 開發環境直接使用生產資料庫 | 資料遺失或測試失誤。 | 使用環境變數切換 SQLALCHEMY_DATABASE_URI / DATABASE_URL,或使用 Docker Compose 分離。 |
最佳實踐:
- 型別註解 + Pydantic:讓資料驗證與序列化自動化,減少手寫驗證程式。
- 分層架構:
router → service → repository,保持業務邏輯與資料存取分離。 - 依賴注入:FastAPI 的
Depends可讓測試變得更簡單,Flask 則可透過 Flask‑Injector。 - 單元測試:使用
pytest+httpx(FastAPI)或FlaskClient(Flask)寫測試,確保路由、驗證、錯誤處理都正確。 - 容器化部署:撰寫
Dockerfile,搭配uvicorn --host 0.0.0.0或gunicorn -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,體驗兩者在 開發速度、效能、文件產生 方面的差異。透過實作,你將能更自信地在不同情境下選擇最合適的框架,為未來的微服務或大型系統奠定堅實基礎。