FastAPI进阶教程

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 + 异步操作 数据库增删改查、表模型定义

🔑 进阶提示:在实际项目中,建议将数据库会话依赖项、模型类、路由处理函数分别放在不同的模块中,以保持代码结构的清晰和可维护性。

相关推荐
JQLvopkk1 小时前
C# 工业级上位机:交互实战
开发语言·c#·交互
张立立1 小时前
震惊!用Python每天早上8点,我准时给女神发早安,只因这个脚本…
后端·python
m0_736439301 小时前
Workerman5.0协程实战:PHP高并发新标准
jvm·数据库·python
2301_818008441 小时前
golang如何实现消息过滤路由_golang消息过滤路由实现要点
jvm·数据库·python
CHANG_THE_WORLD1 小时前
<Fluent Python > 2. 第二章:序列的数组
网络·windows·python
jimy11 小时前
C语言中的 “size_t ”类型
c语言·开发语言
techdashen1 小时前
Cloudflare 如何用 Rust 构建一个高性能解释器
开发语言·后端·rust
2401_831419441 小时前
Python分类汇总怎么做_Crosstab交叉表与多条件联合频数频率统计
jvm·数据库·python
LucaJu1 小时前
DeepAgents 人工介入实战|LangGraph 实现 Agent 高危工具人工审批
python·langchain·agent·langgraph·deepagents