FastAPI 进阶:深入掌握核心技能
📖 本文是 FastAPI 基础教程的续篇,建议先阅读 FastAPI 基础入门指南 后再继续。
经过基础篇的学习,相信你已经掌握了 FastAPI 的基本用法。本篇将深入讲解中间件 、依赖注入 、ORM数据库操作三个核心主题,这些是构建高性能、可维护 API 服务的必备技能。
一、中间件(Middleware)
1.1 什么是中间件?
中间件 是一个在每次请求进入 FastAPI 应用时都会被执行的函数。它像一道"关卡",在请求到达实际的路由处理函数之前拦截处理,并在响应返回给客户端之前再次执行。
请求 → 中间件1 → 中间件2 → 路由处理函数 → 中间件2 → 中间件1 → 响应
1.2 中间件的执行顺序
中间件的装饰器格式为 @app.middleware("http"),它接收两个参数:
request:请求对象call_next:调用下一个处理流程的函数
python
from fastapi import FastAPI
app = FastAPI()
@app.middleware("http")
async def middleware1(request, call_next):
print("中间件1 start")
# call_next 是关键,它让请求继续流向下一个处理环节
response = await call_next(request)
print("中间件1 end")
return response
@app.middleware("http")
async def middleware2(request, call_next):
print("中间件2 start")
response = await call_next(request)
print("中间件2 end")
return response
@app.middleware("http")
async def middleware3(request, call_next):
print("中间件3 start")
response = await call_next(request)
print("中间件3 end")
return response
@app.get("/")
async def root():
return {"message": "Hello World"}
执行结果分析:
中间件3 start ← 最先遇到
中间件2 start ← 第二个
中间件1 start ← 最后进入
中间件1 end ← 最先结束
中间件2 end ← 第二个结束
中间件3 end ← 最后结束
🔑 核心结论 :中间件开始执行是自下而上 (代码中先定义的先执行),结束是自上而下(类似栈的LIFO特性)。
1.3 中间件的典型应用场景
| 场景 | 说明 |
|---|---|
| 📝 日志记录 | 记录每个请求的路径、IP、耗时等信息 |
| 🔐 身份认证 | 验证请求是否携带有效的 Token |
| 🌐 跨域处理(CORS) | 允许前端跨域访问 API |
| 📊 性能监控 | 统计接口响应时间 |
| 🚫 请求限流 | 防止恶意频繁请求 |
1.4 小结
| 问题 | 答案 |
|---|---|
| 中间件的作用是什么? | 为每个请求添加统一的处理逻辑(记录日志、身份认证、跨域、设置响应头等) |
| 如何定义中间件? | 使用装饰器 @app.middleware("http") |
| 多个中间件的执行顺序? | 开始:自下而上;结束:自上而下 |
二、依赖注入(Dependency Injection)
2.1 为什么需要依赖注入?
假设有三个接口都需要分页功能,按照传统写法:
python
# 接口1:新闻列表
@app.get("/news/list")
async def news_list(skip: int = 0, limit: int = 10):
# 分页逻辑
return {"skip": skip, "limit": limit}
# 接口2:用户列表
@app.get("/user/list")
async def user_list(skip: int = 0, limit: int = 10):
# 分页逻辑
return {"skip": skip, "limit": limit}
# 接口3:文章列表
@app.get("/article/list")
async def article_list(skip: int = 0, limit: int = 10):
# 分页逻辑
return {"skip": skip, "limit": limit}
问题:分页逻辑重复写了三次,如果要修改就得改三处!
2.2 依赖注入的概念
- 依赖项:可重用的组件(函数/类),负责提供某种功能或数据
- 注入:FastAPI 自动帮你调用依赖项,并将结果"注入"到路径操作函数中
依赖注入的优势:
| 优势 | 说明 |
|---|---|
| ✅ 代码复用 | 一次编写,多处使用 |
| 🔓 解耦 | 业务逻辑与基础设施代码分离 |
| 🧪 易于测试 | 轻松地用模拟依赖替换真实依赖进行测试 |
2.3 依赖注入的使用方式
第一步:创建依赖项
python
from fastapi import FastAPI, Query
app = FastAPI()
# 定义一个可以被复用的分页参数依赖项
async def common_parameters(
skip: int = Query(0, ge=0), # 跳过的记录数,最小为0
limit: int = Query(10, le=60) # 返回的记录数,最大为60
):
"""
复用性很高的分页参数逻辑
"""
return {"skip": skip, "limit": limit}
第二步:注入到路由处理函数
python
from fastapi.params import Depends
# 新闻列表使用分页参数
@app.get("/news/news_list")
async def news_list(commons = Depends(common_parameters)):
return commons
# 用户列表也使用同样的分页参数
@app.get("/user/user_list")
async def user_list(commons = Depends(common_parameters)):
return commons
核心原理 :Depends(common_parameters) 传递的是函数引用(不加括号),FastAPI 在合适的时机自动调用该函数并把返回值注入到路由处理函数中。
2.4 小结
| 问题 | 答案 |
|---|---|
| 依赖注入有什么用? | 抽取可复用的组件,实现代码复用、解耦且可轻松替换依赖项进行测试 |
| 如何使用依赖注入系统? | 创建依赖项 → 导入 Depends → 在路由函数中声明依赖 |
三、ORM 数据库操作
3.1 ORM 简介
ORM(Object-Relational Mapping) ,即对象关系映射,是一种将面向对象编程语言 与关系型数据库连接起来的技术。

传统 SQL 操作:
python
cursor.execute("SELECT * FROM book WHERE id = 1")
result = cursor.fetchone()
ORM 操作:
python
book = db.get(Book, 1) # 像操作对象一样操作数据库
| 传统 SQL | ORM |
|---|---|
| 手动编写 SQL 字符串 | 通过 Python 对象操作 |
| 容易出现 SQL 注入风险 | 参数绑定,自动防止注入 |
| 字段映射需要手动处理 | 对象属性直接对应表字段 |
ORM 的优势:
- ✅ 减少重复的 SQL 代码
- ✅ 代码更简洁易读
- ✅ 自动处理数据库连接和事务
- ✅ 自动防止 SQL 注入攻击
3.2 SQLAlchemy 异步 ORM 使用流程
本教程使用 SQLAlchemy 作为 ORM 框架,配合 aiomysql 实现异步 MySQL 操作。
环境安装
powershell
# Windows
pip install sqlalchemy aiomysql
# macOS/Linux
pip install "sqlalchemy[asyncio]" aiomysql
数据库准备
sql
-- 创建测试数据库
CREATE DATABASE fastapi_first CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
3.3 数据库建表
步骤一:创建异步数据库引擎
python
from sqlalchemy.ext.asyncio import create_async_engine
ASYNC_DATABASE_URL = "mysql+aiomysql://用户名:密码@localhost:3306/数据库名?charset=utf8"
async_engine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True, # 输出SQL日志,方便调试
pool_size=10, # 连接池中保持的持久连接数
max_overflow=20 # 连接池允许创建的额外连接数
)
步骤二:定义模型类
python
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, Float, DateTime, func
class Base(DeclarativeBase):
"""基类:包含创建时间和更新时间"""
create_time: Mapped[datetime] = mapped_column(
DateTime,
insert_default=func.now(), # 插入时默认当前时间
default=func.now, # 默认值
comment="创建时间"
)
update_time: Mapped[datetime] = mapped_column(
DateTime,
insert_default=func.now(),
onupdate=func.now(), # 更新时自动修改
default=func.now,
comment="更新时间"
)
class Book(Base):
"""书籍表模型类"""
__tablename__ = "book" # 数据库表名
id: Mapped[int] = mapped_column(primary_key=True, comment="书籍id")
bookname: Mapped[str] = mapped_column(String(255), comment="书名")
author: Mapped[str] = mapped_column(String(255), comment="作者")
price: Mapped[float] = mapped_column(Float, comment="价格")
publisher: Mapped[str] = mapped_column(String(255), comment="出版社")
步骤三:使用 Lifespan 管理生命周期(推荐方式)
⚠️ 注意:
@app.on_event("startup")在 FastAPI 新版本中已被弃用,应使用lifespan事件处理器。
python
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
应用生命周期管理器
- yield 之前:应用启动时执行(数据库初始化等)
- yield 之后:应用关闭时执行(关闭连接等)
"""
# 启动时建表
await create_tables()
yield
# 关闭时清理资源(如有需要)
app = FastAPI(lifespan=lifespan)
async def create_tables():
"""创建数据库表"""
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
完整建表代码:
python
from datetime import datetime
from contextlib import asynccontextmanager
from fastapi import FastAPI
from sqlalchemy import DateTime, func, String, Float
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理"""
await create_tables()
yield
app = FastAPI(lifespan=lifespan)
# 1、创建异步引擎
ASYNC_DATABASE_URL = "mysql+aiomysql://root:123456@localhost:3306/fastapi_first?charset=utf8"
async_engine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True,
pool_size=10,
max_overflow=20
)
# 2、定义模型类
class Base(DeclarativeBase):
create_time: Mapped[datetime] = mapped_column(DateTime, insert_default=func.now(), default=func.now, comment="创建时间")
update_time: Mapped[datetime] = mapped_column(DateTime, insert_default=func.now(), default=func.now, onupdate=func.now(), comment="更新时间")
class Book(Base):
__tablename__ = "book"
id: Mapped[int] = mapped_column(primary_key=True, comment="书籍id")
bookname: Mapped[str] = mapped_column(String(255), comment="书名")
author: Mapped[str] = mapped_column(String(255), comment="作者")
price: Mapped[float] = mapped_column(Float, comment="价格")
publisher: Mapped[str] = mapped_column(String(255), comment="出版社")
# 3、建表函数
async def create_tables():
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
@app.get("/")
async def root():
return {"message": "Hello World"}
运行程序后,可以在数据库中看到 book 表已成功创建。
3.4 路由中集成 ORM
创建数据库会话依赖项
python
from sqlalchemy.ext.asyncio import async_sessionmaker, AsyncSession
# 创建异步会话工厂
AsyncSessionLocal = async_sessionmaker(
bind=async_engine, # 绑定数据库引擎
class_=AsyncSession, # 指定会话类
expire_on_commit=False # 会话对象不过期,避免重复查询
)
# 依赖项:获取数据库会话
async def get_database():
async with AsyncSessionLocal() as session:
try:
yield session # 返回会话给路由处理函数
await session.commit() # 无异常,提交事务
except Exception:
await session.rollback() # 有异常,回滚事务
raise
finally:
await session.close() # 关闭会话
在路由中使用
python
from fastapi import Depends
from sqlalchemy import select
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_database)):
"""查询所有图书"""
result = await db.execute(select(Book))
books = result.scalars().all()
return books
💡 关键理解 :
get_database传递的是函数引用(不加括号),FastAPI 会在每次请求时调用它,创建独立的数据库会话。
3.5 数据库操作详解
查询操作
核心语法 :await db.execute(select(模型类))
| 操作 | 方法 | 说明 |
|---|---|---|
| 查询所有 | select(Book) + scalars().all() |
返回列表 |
| 查询第一条 | scalars().first() |
返回单条或 None |
| 按主键查 | db.get(Book, id) |
高效的主键查询 |
| 单条或空 | scalar_one_or_none() |
配合条件查询使用 |
实战示例:
python
# 1. 查询所有图书
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book))
books = result.scalars().all()
return books
# 2. 查询第一条
@app.get("/book/first")
async def get_first_book(db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book))
book = result.scalars().first()
return book
# 3. 按主键查询
@app.get("/book/{book_id}")
async def get_book_by_id(book_id: int, db: AsyncSession = Depends(get_database)):
book = await db.get(Book, book_id)
return book
添加测试数据:
sql
INSERT INTO book (bookname, author, price, publisher, create_time, update_time) VALUES
('活着', '余华', 39.0, '作家出版社', NOW(), NOW()),
('平凡的世界', '路遥', 78.0, '北京十月文艺出版社', NOW(), NOW()),
('百年孤独', '加西亚·马尔克斯', 56.8, '南海出版公司', NOW(), NOW()),
('解忧杂货店', '东野圭吾', 42.0, '南海出版公司', NOW(), NOW());
条件查询
使用 .where() 方法构建查询条件:
python
# 按ID精确查询
@app.get("/book/search/{book_id}")
async def search_book(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
# 价格大于等于某值
@app.get("/book/price/{min_price}")
async def search_by_price(min_price: float, db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book).where(Book.price >= min_price))
books = result.scalars().all()
return books
# 模糊查询 - 作者名以"曹"开头
@app.get("/book/author")
async def search_by_author(db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book).where(Book.author.like("曹%")))
books = result.scalars().all()
return books
# 组合条件 - 作者以"曹"开头且价格大于100
@app.get("/book/complex")
async def complex_query(db: AsyncSession = Depends(get_database)):
result = await db.execute(
select(Book).where(
Book.author.like("曹%") & (Book.price > 100)
)
)
books = result.scalars().all()
return books
条件操作符说明:
| 操作符 | 说明 | 示例 |
|---|---|---|
== |
等于 | Book.id == 1 |
> / < / >= / <= |
比较 | Book.price >= 50 |
like() |
模糊匹配 | Book.author.like("曹%") |
& |
与(AND) | 条件1 & 条件2 |
| ` | ` | 或(OR) |
~ |
非(NOT) | ~条件 |
in_() |
包含 | Book.id.in_([1,2,3]) |
聚合查询
使用 func 函数进行统计:
python
from sqlalchemy import func
@app.get("/book/stats")
async def get_stats(db: AsyncSession = Depends(get_database)):
# 统计数量
count_result = await db.execute(select(func.count(Book.id)))
total = count_result.scalar()
# 平均价格
avg_result = await db.execute(select(func.avg(Book.price)))
avg_price = avg_result.scalar()
# 最高价格
max_result = await db.execute(select(func.max(Book.price)))
max_price = max_result.scalar()
return {
"total_books": total,
"avg_price": avg_price,
"max_price": max_price
}
常用聚合函数:
| 函数 | 说明 |
|---|---|
func.count() |
统计数量 |
func.avg() |
平均值 |
func.max() |
最大值 |
func.min() |
最小值 |
func.sum() |
求和 |
分页查询
python
@app.get("/book/pagination")
async def get_pagination(
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, "page": page, "page_size": page_size}
新增操作
核心步骤 :创建 ORM 对象 → db.add() → await db.commit()
python
from pydantic import BaseModel
class BookCreate(BaseModel):
"""请求体模型:接收前端传来的图书信息"""
id: int
bookname: str
author: str
price: float
publisher: str
@app.post("/book/add")
async def add_book(book: BookCreate, db: AsyncSession = Depends(get_database)):
"""新增图书"""
# 将请求体数据转换为 ORM 对象
book_obj = Book(**book.__dict__)
# 添加到事务
db.add(book_obj)
# 提交到数据库(依赖项会自动 commit)
return book
更新操作
核心步骤:查询 → 修改属性 → commit
python
from pydantic import BaseModel
class BookUpdate(BaseModel):
"""更新请求体"""
bookname: str
author: str
price: float
publisher: str
@app.put("/book/update/{book_id}")
async def update_book(
book_id: int,
data: BookUpdate,
db: AsyncSession = Depends(get_database)
):
"""更新图书信息"""
# 1. 查询
book = await db.get(Book, book_id)
if book is None:
raise HTTPException(status_code=404, detail="图书不存在")
# 2. 修改属性
book.bookname = data.bookname
book.author = data.author
book.price = data.price
book.publisher = data.publisher
# 3. 提交(依赖项会自动 commit)
return book
删除操作
核心步骤 :查询 → db.delete() → commit
python
@app.delete("/book/delete/{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="图书不存在")
# 删除并提交
await db.delete(db_book)
return {"message": "删除成功"}
3.6 小结:ORM 操作核心模板
┌─────────────────────────────────────────────────────────────┐
│ ORM 操作速查表 │
├──────────┬──────────────────────────────────────────────────┤
│ 查询 │ result = await db.execute(select(Book)) │
│ │ books = result.scalars().all() │
├──────────┼──────────────────────────────────────────────────┤
│ 按主键查 │ book = await db.get(Book, id) │
├──────────┼──────────────────────────────────────────────────┤
│ 条件查 │ .where(Book.price >= 100) │
├──────────┼──────────────────────────────────────────────────┤
│ 新增 │ db.add(book_obj) → await db.commit() │
├──────────┼──────────────────────────────────────────────────┤
│ 更新 │ book.price = 200 → await db.commit() │
├──────────┼──────────────────────────────────────────────────┤
│ 删除 │ await db.delete(book) → await db.commit() │
└──────────┴──────────────────────────────────────────────────┘
四、总结
本篇教程涵盖了 FastAPI 进阶的三大核心主题:
| 主题 | 核心概念 | 应用场景 |
|---|---|---|
| 中间件 | @app.middleware("http") 装饰器 |
日志记录、身份认证、跨域、性能监控 |
| 依赖注入 | Depends() 函数 |
代码复用、解耦、测试友好 |
| ORM | SQLAlchemy + 异步操作 | 数据库增删改查、表模型定义 |
🔑 进阶提示:在实际项目中,建议将数据库会话依赖项、模型类、路由处理函数分别放在不同的模块中,以保持代码结构的清晰和可维护性。