本文 AI 產出,尚未審核
FastAPI
單元:請求與回應(Request & Response)
主題:Request 物件(from fastapi import Request)
簡介
在 Web 應用程式中,伺服器要能正確取得、解析、驗證客戶端送來的資料,才能完成後續的業務邏輯。FastAPI 內建的 Request 物件正是負責這件事的關鍵工具。它不只是把 HTTP 請求的資訊封裝成一個 Python 物件,更提供了多種便利的屬性與方法,讓開發者可以輕鬆取用標頭、查詢參數、路徑參數、JSON、表單、檔案上傳等資料。
本篇文章將從 概念、使用方式、常見陷阱與最佳實踐,一步步帶領讀者掌握 Request 物件的核心功能,並透過實作範例說明它在實務專案中的應用情境,適合 初學者 想快速上手,也能為 中階開發者 提供更深入的技巧與思考。
核心概念
1. 為什麼需要 Request 物件?
- 抽象化 HTTP:直接操作
starlette.requests.Request(FastAPI 底層框架)較為繁瑣,FastAPI 直接把它注入到路由函式中,讓我們只需要關注資料本身。 - 支援非同步:
Request支援async/await,可在非同步路由中安全讀取資料,避免阻塞 I/O。 - 統一介面:不論是
GET、POST、PUT、PATCH、DELETE,或是 WebSocket,都能透過相同的屬性取得資訊,降低學習成本。
2. 基本使用方式
在 FastAPI 中,只要在路由函式的參數列表加入 request: Request,框架就會自動把當前的請求實例注入進來:
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/info")
async def get_info(request: Request):
# 直接使用 request 物件
client_host = request.client.host
return {"client_ip": client_host}
小技巧:如果只需要取得特定資訊(例如標頭),可以直接使用 依賴注入(Dependency Injection)把
Request包裝成子函式,讓程式碼更具可讀性。
3. 常用屬性與方法
| 屬性 / 方法 | 說明 | 範例 |
|---|---|---|
request.method |
HTTP 方法(GET、POST 等) | if request.method == "POST": ... |
request.url |
完整的 URL 物件,可直接取 path、query 等 |
request.url.path |
request.headers |
以 Headers 物件呈現的所有標頭 |
request.headers["User-Agent"] |
request.query_params |
查詢字串(?key=value)的 MultiDict |
request.query_params.get("page") |
await request.json() |
解析 JSON 主體(非同步) | data = await request.json() |
await request.body() |
取得原始位元組資料 | raw = await request.body() |
await request.form() |
解析 application/x-www-form-urlencoded 或 multipart/form-data(表單) |
form = await request.form() |
await request.stream() |
以串流方式讀取大型檔案 | async for chunk in request.stream(): ... |
request.client |
客戶端 IP 與埠 | request.client.host |
request.cookies |
Cookie 集合 | request.cookies.get("session_id") |
注意:
await只能在async定義的路由或依賴函式中使用;若使用同步函式(def),則必須改用request.json()等同步版本(FastAPI 會自動轉換),但 建議 盡量使用非同步方式以發揮 FastAPI 的效能優勢。
4. 範例:取得不同來源的資料
以下示範 5 個實務中常見的情境,說明如何透過 Request 取得所需資訊。每段程式碼皆加入詳細註解,方便讀者快速理解。
範例 1️⃣ 讀取 JSON 主體
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
@app.post("/items")
async def create_item(request: Request):
"""
取得請求的 JSON 主體,並檢查必填欄位。
"""
try:
payload = await request.json() # 解析 JSON
except Exception:
raise HTTPException(status_code=400, detail="Invalid JSON")
# 確認必填欄位
if "name" not in payload:
raise HTTPException(status_code=422, detail="Missing 'name' field")
return {"msg": f"Item '{payload['name']}' created"}
範例 2️⃣ 讀取表單資料與檔案上傳
from fastapi import FastAPI, Request, UploadFile, HTTPException
app = FastAPI()
@app.post("/upload")
async def upload_file(request: Request):
"""
同時取得表單欄位與上傳的檔案。
前端需使用 multipart/form-data。
"""
form = await request.form()
description = form.get("description", "No description")
upload: UploadFile = form.get("file") # 取得檔案物件
if not upload:
raise HTTPException(status_code=400, detail="File missing")
# 讀取檔案內容(示範,實務上建議使用流式寫入硬碟)
content = await upload.read()
size_kb = len(content) / 1024
return {
"filename": upload.filename,
"size_kb": round(size_kb, 2),
"description": description,
}
範例 3️⃣ 取得查詢參數與路徑參數的結合
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(user_id: int, request: Request):
"""
透過 request 直接存取 query string。
例如:/users/5?detail=full
"""
detail_level = request.query_params.get("detail", "summary")
# 假設有一個 get_user_from_db 的函式
user = {"id": user_id, "name": "Alice"}
if detail_level == "full":
user["email"] = "alice@example.com"
user["roles"] = ["admin", "editor"]
return user
範例 4️⃣ 讀取原始位元組(例如自訂二進位協定)
from fastapi import FastAPI, Request, Response
app = FastAPI()
@app.post("/binary")
async def binary_endpoint(request: Request):
"""
接收任意二進位資料,回傳其 SHA256 雜湊。
"""
raw_body = await request.body() # 取得原始位元組
import hashlib
digest = hashlib.sha256(raw_body).hexdigest()
return {"sha256": digest}
範例 5️⃣ 取得客戶端資訊與自訂標頭
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/client-info")
async def client_info(request: Request):
"""
回傳呼叫端的 IP、User-Agent 與自訂標頭 X-Request-ID。
"""
client_ip = request.client.host
user_agent = request.headers.get("User-Agent", "unknown")
request_id = request.headers.get("X-Request-ID", "none")
return {
"client_ip": client_ip,
"user_agent": user_agent,
"request_id": request_id,
}
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
同步呼叫 await request.json() |
在同步路由 (def) 中使用 await 會拋出 SyntaxError。 |
使用 async def,或改用 request.json()(非同步包裝) |
| 重複讀取請求主體 | await request.body()、await request.json()、await request.form() 均會消耗 stream,第二次讀取會得到空值。 |
先決定一次性讀取方式,或使用 request.state 暫存已解析的資料 |
| 忽略大型檔案的記憶體使用 | 直接 await upload.read() 會一次把整個檔案載入記憶體,對大檔案會造成 OOM。 |
使用 await upload.read(chunk_size) 或 await request.stream() 逐塊寫入磁碟 |
| 未處理缺失標頭或參數 | 直接 request.headers["X-My-Header"] 若不存在會拋 KeyError。 |
使用 .get() 並提供預設值,或自行檢查 if "X-My-Header" in request.headers |
| 直接信任客戶端 IP | 透過 request.client.host 取得的 IP 可能是 proxy(例如 Nginx)後的內部位址。 |
配合 trusted_hosts 或 X-Forwarded-For 標頭,使用 starlette.middleware.trustedhost.TrustedHostMiddleware |
最佳實踐
- 統一使用非同步路由:除非有特別需求,盡量以
async def定義,確保await request.xxx()可正常運作。 - 一次性解析:若同時需要 JSON 與表單,先判斷
Content-Type再呼叫對應的解析方法,避免重複讀取。 - 使用
request.state儲存共享資料:在自訂中介層(middleware)裡解析請求後,可把結果放入request.state,供後續路由共用,減少重複運算。 - 妥善驗證與清理:所有從
Request取得的資料,都應該走 Pydantic 或 FastAPI 的依賴驗證,避免直接信任。 - 日誌與追蹤:在中介層記錄
request.method、request.url.path、request.headers(過濾敏感資訊)與request.client.host,有助於除錯與安全稽核。
實際應用場景
| 場景 | 為何需要 Request |
典型實作 |
|---|---|---|
| OAuth2 授權流程 | 必須讀取 Authorization 標頭、redirect_uri 查詢參數,並在回傳時設定 Set-Cookie。 |
中介層解析 token、驗證後把使用者資訊寫入 request.state.user |
| Webhooks 接收 | 第三方服務往往以自訂 JSON 或 XML 格式送來,且可能附帶簽章(Signature)在標頭。 | 使用 await request.body() 取得原始資料,計算 HMAC 再比對 |
| 檔案上傳與即時處理 | 大檔案需以串流方式讀取,避免一次載入記憶體。 | async for chunk in request.stream(): process(chunk) |
| 多租戶 (Multi‑tenant) 系統 | 從 Host 或自訂標頭決定租戶,並在每個請求內部設定資料庫連線。 |
中介層 tenant = request.headers.get("X-Tenant-ID") → request.state.db = get_db(tenant) |
| API 速率限制 (Rate Limiting) | 需要取得 client IP、API key 等資訊,計算每分鐘呼叫次數。 | 中介層讀取 request.client.host、request.headers["X-API-Key"],寫入 Redis 計數器 |
總結
Request物件是 FastAPI 與 Starlette 之間的橋樑,提供了完整且一致的介面,讓開發者能夠輕鬆存取 標頭、查詢參數、路徑參數、JSON、表單、檔案、原始位元組 等資訊。- 正確使用
async/await、避免重複讀取主體、妥善驗證與清理,是避免常見錯誤的關鍵。 - 透過 中介層 +
request.state,可以在全局層面統一處理認證、租戶、速率限制等需求,提高程式碼的可維護性與可重用性。 - 在實務專案中,
Request常被用於 OAuth、Webhooks、檔案上傳、Multi‑tenant 等場景,掌握它的使用技巧將直接提升 API 的彈性與安全性。
**把握
Request的力量,你的 FastAPI 應用將更具可讀性、可測試性,也更能因應日益複雜的業務需求。**祝你開發順利,持續探索 FastAPI 的無限可能!