跳转至

FastAPI 进阶 — 中间件与依赖注入


一句话说明

FastAPI 的中间件拦截所有请求做横切处理(日志/限流/鉴权),依赖注入(Depends)把公共逻辑(数据库连接/当前用户)自动传给路由函数,是构建生产级 API 的关键技巧。


安装与配置

# pip 安装
pip install "fastapi[standard]"      # 含 uvicorn,当前 0.115+

# 启动开发服务器
uvicorn main:app --reload            # 热重载模式

中间件

基础中间件

from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
import time

app = FastAPI()

# 自定义中间件:记录请求耗时
class TimingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start = time.time()                          # 记录开始时间
        response = await call_next(request)          # 调用下一层(真正的路由)
        duration = time.time() - start               # 计算耗时
        response.headers["X-Process-Time"] = f"{duration:.3f}s"  # 加到响应头
        return response

app.add_middleware(TimingMiddleware)   # 注册中间件

# CORS 中间件(允许前端跨域请求)
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
    CORSMiddleware,
    allow_origins     = ["http://localhost:3000"],  # 允许的前端域名
    allow_credentials = True,
    allow_methods     = ["*"],                      # 允许所有 HTTP 方法
    allow_headers     = ["*"],
)

限流中间件

from fastapi import FastAPI, Request, HTTPException
from collections import defaultdict
import time

app = FastAPI()

# 简单的 IP 限流(生产用 slowapi 或 redis)
request_counts = defaultdict(list)    # IP -> 请求时间戳列表

class RateLimitMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        client_ip  = request.client.host             # 获取客户端 IP
        now        = time.time()
        window     = 60                              # 时间窗口:60秒
        max_reqs   = 100                             # 最多 100 次请求

        # 清除窗口外的记录
        request_counts[client_ip] = [
            t for t in request_counts[client_ip] if now - t < window
        ]

        if len(request_counts[client_ip]) >= max_reqs:
            raise HTTPException(429, "请求太频繁,请稍后重试")

        request_counts[client_ip].append(now)        # 记录本次请求
        return await call_next(request)

app.add_middleware(RateLimitMiddleware)

依赖注入(Depends)

基本依赖

from fastapi import FastAPI, Depends, HTTPException, Header
from typing import Annotated

app = FastAPI()

# 定义依赖函数
def get_db():
    """数据库连接依赖(用完自动关闭)"""
    db = {"connection": "fake_db_connection"}   # 实际用 SQLAlchemy Session
    try:
        yield db                                 # yield 前:建立连接
    finally:
        pass                                     # yield 后:关闭连接(放在 finally)

def verify_token(x_token: Annotated[str, Header()]) -> str:
    """从 Header 验证 API Token"""
    if x_token != "secret-token-123":
        raise HTTPException(401, "Token 无效")
    return x_token

# 在路由中使用依赖
@app.get("/patients/{patient_id}")
async def get_patient(
    patient_id: int,
    db:    Annotated[dict, Depends(get_db)],         # 注入数据库
    token: Annotated[str, Depends(verify_token)],    # 注入鉴权
):
    # 此时 db 和 token 都已准备好
    return {"patient_id": patient_id, "db": db["connection"]}

路由级权限控制

from fastapi import FastAPI, Depends, Security

# 定义当前用户依赖(从 JWT 解析)
async def get_current_user(token: Annotated[str, Header(alias="authorization")]):
    # 实际应解析 JWT
    if token == "Bearer admin-token":
        return {"user_id": 1, "role": "admin"}
    elif token == "Bearer user-token":
        return {"user_id": 2, "role": "user"}
    raise HTTPException(401, "未登录")

def require_admin(user = Depends(get_current_user)):
    """只允许管理员访问的依赖"""
    if user["role"] != "admin":
        raise HTTPException(403, "权限不足,需要管理员权限")
    return user

@app.delete("/patients/{pid}", dependencies=[Depends(require_admin)])
async def delete_patient(pid: int):
    return {"message": f"患者 {pid} 已删除"}

# 路由组级别应用依赖
from fastapi import APIRouter
admin_router = APIRouter(prefix="/admin", dependencies=[Depends(require_admin)])

常见报错与解决

报错原因解决
422 Unprocessable Entity请求参数格式错检查 Header/Body 字段名和类型
依赖不执行忘记 Depends() 包装确保用 Annotated[T, Depends(func)]
中间件顺序问题中间件执行顺序是逆序注册先 add 的后执行,注意 CORS 要最先 add

速查表

操作代码
自定义中间件继承 BaseHTTPMiddleware,实现 dispatch
注册中间件app.add_middleware(MyMiddleware)
CORSapp.add_middleware(CORSMiddleware, ...)
基本依赖Depends(my_func)
Header 依赖Annotated[str, Header()]
yield 依赖函数中用 yield,之后为清理代码
路由级依赖@app.get("/path", dependencies=[Depends(...)])
路由组依赖APIRouter(dependencies=[Depends(...)])