一、引言
FastAPI 作为当前 Python 领域最炙手可热的异步 Web 框架,凭借其高性能、自动生成 API 文档以及对异步的原生支持,赢得了大量开发者的青睐。然而,仅仅掌握路由和请求响应的基础用法,远不足以构建一个健壮的企业级应用。中间件(Middleware) 、依赖注入(Dependency Injection) 和 ORM(对象关系映射) 正是进阶路上必须跨越的三座大山。
本文将基于 FastAPI 官方最佳实践,结合 SQLAlchemy 2.0 异步特性,从零搭建一个完整的图书管理 API,涵盖统一日志处理、用户认证依赖、以及完整的数据库增删改查、分页、聚合查询等核心功能。所有代码均可在实际项目中直接复用。
二、中间件(Middleware)------ 为所有请求添加"守门员"
2.1 什么是中间件?
中间件是一个在每次请求进入 FastAPI 应用时都会被执行的函数。它在请求到达路径操作函数之前运行,并且在响应返回给客户端之前再执行一次。你可以把它想象成一个"守门员",对所有进出请求进行统一处理。
2.2 中间件的典型应用场景
-
记录每个请求的耗时和日志
-
统一身份认证(虽然更推荐用依赖注入,但中间件也可做全局鉴权)
-
跨域资源共享(CORS)配置
-
统一添加或修改响应头(如
X-Request-ID) -
性能监控和指标收集
2.3 如何定义一个中间件?
使用 @app.middleware("http") 装饰器,函数签名为 async def middleware(request: Request, call_next)。内部通过 await call_next(request) 调用下一个中间件或最终的路由处理函数,并获取响应对象。
from fastapi import FastAPI, Request
import time
app = FastAPI()
@app.middleware("http")
async def log_requests(request: Request, call_next):
# 请求前处理
start_time = time.time()
print(f"收到请求: {request.method} {request.url.path}")
# 执行后续逻辑(路由处理函数或下一个中间件)
response = await call_next(request)
# 响应后处理
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
print(f"请求处理完成,耗时: {process_time:.4f}秒")
return response
2.4 多个中间件的执行顺序
顺序是"自下而上" ------ 即先注册的后执行(类似洋葱模型)。例如:
@app.middleware("http")
async def middleware_a(request, call_next):
print("A 开始")
response = await call_next(request)
print("A 结束")
return response
@app.middleware("http")
async def middleware_b(request, call_next):
print("B 开始")
response = await call_next(request)
print("B 结束")
return response
2.4.1核心结论
FastAPI 中后注册的中间件会先执行前置逻辑 ,收尾逻辑则逆序执行。 你代码里先定义 middleware_a、再定义 middleware_b:
-
注册顺序:
A→B -
执行打印顺序:
B 开始
A 开始路由接口业务逻辑执行
A 结束
B 结束
2.4.2 原理说明
FastAPI 会把多个 HTTP 中间件层层嵌套包装:
B(middleware_a(路由函数))
调用 call_next(request) 就是进入内层中间件 / 路由:
- 先走外层 B 前置:
B 开始 - 进入内层 A 前置:
A 开始 - 走到真正接口视图函数
- 接口返回后,先退出 A 后置:
A 结束 - 最后退出 B 后置:
B 结束
2.4.3 直观流程图
请求进来
↓
middleware_b 【前置】打印 B 开始
↓ call_next()
middleware_a 【前置】打印 A 开始
↓ call_next()
路由处理函数执行业务
↑ 响应返回
middleware_a 【后置】打印 A 结束
↑ 回到上一层
middleware_b 【后置】打印 B 结束
↓
返回响应给客户端
三、依赖注入(Dependency Injection)------ 告别重复代码
3.1 依赖注入系统的作用
依赖注入是一种设计模式,FastAPI 内置了极其优雅的实现。它的核心思想是:将可复用的逻辑(如数据库会话、用户身份校验、配置对象)抽取为依赖项,由框架自动调用并注入到路径操作函数中。
主要优势:
-
代码复用:一次编写,多处使用
-
解耦:业务逻辑与基础设施(数据库、缓存等)分离
-
易于测试:可以轻松用模拟对象替换真实依赖进行单元测试
3.2 使用步骤
-
创建一个可调用对象(函数或类)作为依赖项
-
在路径操作函数参数中,使用
Depends(依赖项)声明 -
FastAPI 自动解析并注入依赖项的结果
3.3 实战:用户认证依赖项
假设我们有多个接口需要验证 JWT Token,并返回当前用户信息。
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 模拟用户数据
fake_users_db = {
"admin": {"username": "admin", "role": "superadmin"}
}
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的认证凭据",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, "SECRET_KEY", algorithms=["HS256"])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = fake_users_db.get(username)
if user is None:
raise credentials_exception
return user
# 在路由中使用
@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
return current_user
这样一来,所有需要认证的接口只需声明 current_user = Depends(get_current_user) 即可,完全避免了在每个函数里重复编写解析 Token 的代码。
四、ORM(对象关系映射)------ 用面向对象的方式操作数据库
4.1 ORM 简介
ORM 将数据库表映射为 Python 类,将表中的行映射为类的实例(对象)。开发者通过操作对象和方法,即可完成增删改查,无需手写 SQL。在 FastAPI 异步生态中,SQLAlchemy 2.0 是绝对的首选,它原生支持 async/await,性能强大,企业应用广泛。
ORM 带来的好处:
-
减少重复的 SQL 拼接代码
-
代码更简洁、可读性更强
-
自动管理数据库连接和事务
-
有效防止 SQL 注入攻击
4.2 环境准备与引擎创建
首先安装依赖:
pip install fastapi uvicorn sqlalchemy[asyncio] aiomysql
创建异步数据库引擎(以 MySQL 为例):
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy import Column, Integer, String, DateTime, func
from datetime import datetime
ASYNC_DATABASE_URL = "mysql+aiomysql://root:123456@localhost:3306/fastapi_test?charset=utf8"
async_engine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True, # 打印 SQL 日志,便于调试
pool_size=10, # 连接池保持的持久连接数
max_overflow=20 # 允许额外创建的连接数
)
AsyncSessionLocal = sessionmaker(
async_engine, class_=AsyncSession, expire_on_commit=False
)
4.3 定义模型基类和模型类
我们定义一个基类,包含所有表共有的 create_time 和 update_time 字段,然后为图书表创建模型。
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, DateTime, func
class Base(DeclarativeBase):
create_time: Mapped[datetime] = mapped_column(
DateTime, insert_default=func.now(), default=datetime.now, comment="创建时间"
)
update_time: Mapped[datetime] = mapped_column(
DateTime,
insert_default=func.now(),
onupdate=func.now(),
default=datetime.now,
comment="修改时间"
)
class Book(Base):
__tablename__ = "book"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
bookname: Mapped[str] = mapped_column(String(255), nullable=False)
author: Mapped[str] = mapped_column(String(255), nullable=False)
price: Mapped[float] = mapped_column(nullable=True) # 价格字段,为后续查询演示扩展
4.4 创建数据库表(应用启动时)
利用 FastAPI 的 startup 事件,在应用启动时自动建表。
async def create_tables():
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
@app.on_event("startup")
async def startup_event():
await create_tables()
4.5 获取数据库会话的依赖项

每次请求需要独立的事务会话,我们将其封装为依赖项,并自动管理会话的关闭。
4.5.1异步会话工厂 AsyncSessionLocal
AsyncSessionLocal = async_sessionmaker(
bind=async_engine, # 绑定全局异步数据库引擎(提前初始化的async数据库连接引擎)
class_=AsyncSession, # 指定使用SQLAlchemy异步会话类AsyncSession
expire_on_commit=False # commit之后,模型对象不会过期,不用重复查库;方便查询后继续使用ORM对象
)
async_sessionmaker:SQLAlchemy 异步版会话工厂,用来批量创建数据库会话expire_on_commit=False常用场景:查询拿到数据 commit 后,依然能正常读取模型属性,不会报lazy loading过期报错
4.5.2依赖函数 get_database(最关键)
这是 FastAPI 依赖项,每来一次 HTTP 请求,就执行一次,生成全新独立 session
async def get_database():
async with AsyncSessionLocal() as session:
try:
yield session # 把会话给到接口函数使用(yield实现依赖的上下文暂停)
await session.commit() # 接口无报错:自动提交事务
except Exception:
await session.rollback() # 接口抛异常:事务回滚,保证数据一致性
raise # 重新抛出异常,让FastAPI正常返回错误响应
finally:
await session.close() # 无论成功失败,最终强制关闭会话释放连接
4.5.3路由层:Depends 注入使用
@app.get("/book/books")
async def get_book_list(
db: AsyncSession = Depends(get_database)
):
# 使用注入进来的db会话执行查询
result = await db.execute(select(Book))
book_list = result.scalars().all()
return book_list
五、路由中使用 ORM 与数据库操作
5.1 依赖注入数据库会话
核心:创建依赖项 get_database,返回异步数据库会话 AsyncSession,并通过 Depends 注入到路由处理函数。
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
# 创建异步会话工厂
AsyncSessionLocal = async_sessionmaker(async_engine, expire_on_commit=False)
async def get_database() -> AsyncSession:
async with AsyncSessionLocal() as session:
yield session
在路由中:
from fastapi import Depends, FastAPI
from sqlalchemy.ext.asyncio import AsyncSession
app = FastAPI()
@app.get("/books")
async def get_books(db: AsyncSession = Depends(get_database)):
# 使用 db 执行查询
pass
5.2 查询操作
5.2.1 查询所有数据与单条数据
查询所有: select(模型类) → db.execute() → scalars().all()
from sqlalchemy import select
@app.get("/book/get_books")
async def get_book_list(db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book))
books = result.scalars().all()
return books
查询单条: 使用 scalars().first() 或 db.get(模型类, 主键值)
@app.get("/book/get_book")
async def get_book(db: AsyncSession = Depends(get_database)):
# 方式一:提取第一条
# result = await db.execute(select(Book))
# book = result.scalars().first()
# 方式二:通过主键获取
book = await db.get(Book, 1)
return book
5.2.2 查询条件
在 select().where() 中添加条件,支持多种比较和逻辑运算。
-
比较判断 :
==,>,<,>=,<=@app.get("/book/{book_id}")
async def get_book_by_id(book_id: int, db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book).where(Book.id == book_id))
book = result.scalar_one_or_none()
return book -
模糊查询 :
like(),%匹配任意字符,_匹配单个字符@app.get("/book/get_books_by_author")
async def get_books_by_author(db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book).where(Book.author.like("曹%")))
books = result.scalars().all()
return books -
与非查询 :使用
&(与)、|(或)、~(非)@app.get("/book/get_books_filter")
async def get_books_filter(db: AsyncSession = Depends(get_database)):
result = await db.execute(
select(Book).where((Book.author == "曹雪芹") & (Book.price == 200))
)
books = result.scalars().all()
return books -
包含查询 :
in_()@app.get("/book/get_books_by_ids")
async def get_books_by_ids(db: AsyncSession = Depends(get_database)):
id_list = [1, 2, 3, 4, 5, 6]
result = await db.execute(select(Book).where(Book.id.in_(id_list)))
books = result.scalars().all()
return books
5.2.3 聚合查询
使用 func.count(), func.avg(), func.max(), func.min(), func.sum() 进行聚合。
from sqlalchemy import func
@app.get("/book/count")
async def get_count(db: AsyncSession = Depends(get_database)):
# result = await db.execute(select(func.count(Book.id)))
# result = await db.execute(select(func.max(Book.price)))
# result = await db.execute(select(func.sum(Book.price)))
result = await db.execute(select(func.avg(Book.price)))
count = result.scalar()
return count
5.2.4 分页查询
使用 .offset().limit() 实现分页。offset 为跳过的记录数,limit 为每页返回的记录数。
计算公式:offset = (当前页码 - 1) * 每页数量
| 当前页码 | 每页数量(limit) | 跳过数量(offset) |
|---|---|---|
| 1 | 10 | 0 |
| 2 | 10 | 10 |
| 3 | 10 | 20 |
| 4 | 10 | 30 |
@app.get("/book/get_books")
async def get_book_list(
page: int = 1,
page_size: int = 3,
db: AsyncSession = Depends(get_database)
):
skip = (page - 1) * page_size
stmt = select(Book).offset(skip).limit(page_size)
result = await db.execute(stmt)
books = result.scalars().all()
return {"books": books}
5.2.5 查询总结
-
核心思路 :
select()→db.execute()→ 从 ORM 对象获取数据 → 响应结果 -
获取数据方式:
-
scalars().all():获取所有数据 -
scalars().first():获取第一条数据 -
scalar_one_or_none():获取一个或 None(若多于一条则报错) -
scalar():获取标量值(聚合查询常用)
-
5.3 新增操作
步骤:创建 ORM 对象 → db.add() → await db.commit()
from pydantic import BaseModel
class BookBase(BaseModel):
bookname: str
author: str
price: float
@app.post("/book/add_book")
async def add_book(book: BookBase, db: AsyncSession = Depends(get_database)):
book_obj = Book(**book.__dict__) # 或 Book(bookname=book.bookname, ...)
db.add(book_obj)
await db.commit()
return book_obj
5.4 更新操作
步骤:查询获取对象 → 修改属性 → commit
class BookUpdate(BaseModel):
bookname: str
author: str
price: float
@app.put("/book/update_book/{book_id}")
async def update_book(
book_id: int,
data: BookUpdate,
db: AsyncSession = Depends(get_database)
):
book = await db.get(Book, book_id)
if book is None:
raise HTTPException(status_code=404, detail="Book not found")
book.bookname = data.bookname
book.author = data.author
book.price = data.price
await db.commit()
return book
5.5 删除操作
步骤:查询获取对象 → db.delete() → commit
@app.delete("/book/delete_book/{book_id}")
async def delete_book(book_id: int, db: AsyncSession = Depends(get_database)):
db_book = await db.get(Book, book_id)
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
await db.delete(db_book)
await db.commit()
return {"message": "Book deleted"}
六、总结
本文围绕 FastAPI 进阶的三大核心内容展开:
-
中间件:为每个请求/响应添加统一处理逻辑,定义简单,执行顺序自下而上。
-
依赖注入:抽取可复用组件(如数据库会话、认证逻辑),实现解耦与易测性。
-
ORM(SQLAlchemy):通过对象方式操作数据库,避免手写 SQL,提升开发效率与安全性。
ORM 使用全流程速览:
-
安装依赖:
pip install sqlalchemy[asyncio] aiomysql -
创建异步引擎(
create_async_engine) -
定义基类(
DeclarativeBase)和模型类 -
启动时建表(
run_sync(Base.metadata.create_all)) -
路由中注入会话依赖(
Depends(get_database)) -
执行 CRUD:
select()、add()、属性赋值、delete(),最后commit()
掌握这些技能,即可构建健壮、可维护的 FastAPI 企业级应用。
😊

