FastAPI 终极实战:ORM 数据库、RESTful 设计、中间件与依赖注入
这是 FastAPI 系列的最后一篇。你将学会:使用 SQLAlchemy ORM 操作数据库、设计符合 RESTful 规范的 API、理解 HTTP 状态码的语义、模块化路由、依赖注入复用逻辑、中间件统一处理请求、解决跨域问题、以及提供静态文件服务。最终完成一个完整的图书管理 API 项目。
目录
- [ORM 基础:SQLAlchemy 集成](#ORM 基础:SQLAlchemy 集成)
- 1.1 核心组件(Engine、Base、Session)
- 1.2 定义模型
- 1.3 创建表
- 1.4 增删改查操作
- [RESTful API 设计原则](#RESTful API 设计原则)
- 2.1 核心原则:URL 描述资源,HTTP 方法描述操作
- 2.2 示例:学生资源的增删改查
- [HTTP 状态码详解与高频对比](#HTTP 状态码详解与高频对比)
- 3.1 2xx 成功
- 3.2 3xx 重定向
- 3.3 4xx 客户端错误
- 3.4 5xx 服务器错误
- 3.5 易混淆对比(400 vs 422、401 vs 403、307 vs 308、500 vs 503)
- 子路由(模块化)
- 依赖注入(Depends)
- 中间件(Middleware)
- 6.1 作用与实现结构
- 6.2 常见应用:日志、耗时统计、认证
- 6.3 多中间件顺序
- 跨域(CORS)配置
- 静态资源服务
- [综合实战:图书管理 API(完整代码)](#综合实战:图书管理 API(完整代码))
- 总结与系列回顾
1. ORM 基础:SQLAlchemy 集成
SQLAlchemy 是 Python 最流行的 ORM(对象关系映射)库,它将数据库表映射为 Python 类,将行映射为对象,将列映射为属性。
1.1 核心组件
| 组件 | 说明 |
|---|---|
Engine |
数据库连接的核心,管理连接池 |
Base |
模型基类,所有模型继承它 |
Session |
数据库操作会话,类似"工作单元" |
1.2 定义模型
python
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 创建引擎(以 SQLite 为例)
engine = create_engine("sqlite:///./test.db", echo=True)
# 模型基类
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), nullable=False)
age = Column(Integer)
1.3 创建表
python
Base.metadata.create_all(bind=engine)
1.4 增删改查操作
python
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
# 新增单条
user = User(name="张三", age=20)
db.add(user)
db.commit()
db.refresh(user)
# 新增多条
db.add_all([User(name="李四", age=22), User(name="王五", age=25)])
db.commit()
# 查询
all_users = db.query(User).all() # 所有
adults = db.query(User).filter(User.age >= 18).all() # 条件过滤
paged = db.query(User).offset(0).limit(10).all() # 分页
# 修改
user = db.query(User).filter(User.name == "张三").first()
if user:
user.age = 21
db.commit()
# 删除
db.query(User).filter(User.name == "李四").delete()
db.commit()
⚠️ 注意 :所有写操作必须执行
db.commit()才会写入数据库。操作完成后建议关闭 session:db.close(),可使用try/finally确保释放。
2. RESTful API 设计原则
RESTful 是一种 API 设计规范,旨在提高可读性与可维护性。
| 核心原则 | 说明 | 示例 |
|---|---|---|
| URL 描述资源 | 使用名词复数,避免动词 | /users 而不是 /getUser |
| HTTP 方法描述操作 | GET 查询、POST 新增、PUT 修改、DELETE 删除 | GET /users/1 获取 id=1 的用户 |
| 状态码语义化 | 用状态码表达结果类型 | 200 OK, 201 Created, 404 Not Found |
RESTful 示例:
| 请求 | 含义 |
|---|---|
GET /students |
获取所有学生 |
POST /students |
新增一个学生 |
PUT /students/{id} |
修改指定学生 |
DELETE /students/{id} |
删除指定学生 |
3. HTTP 状态码详解与高频对比
3.1 2xx 成功
| 状态码 | 含义 | 使用场景 |
|---|---|---|
| 200 OK | 请求成功 | GET、PUT、DELETE 成功 |
| 201 Created | 创建成功 | POST 创建资源成功后返回,通常带 Location 头 |
| 204 No Content | 成功但无返回体 | DELETE 成功后常用 |
3.2 3xx 重定向
| 状态码 | 含义 | 特点 |
|---|---|---|
| 307 Temporary Redirect | 临时重定向 | 保持请求方法不变 |
| 308 Permanent Redirect | 永久重定向 | 保持请求方法不变 |
3.3 4xx 客户端错误
| 状态码 | 含义 | 典型原因 |
|---|---|---|
| 400 Bad Request | 请求格式错误或缺少必要参数 | 缺少查询参数如 /search 缺少 keyword |
| 401 Unauthorized | 未登录 | 未提供 token 或 token 无效 |
| 403 Forbidden | 已登录但无权限 | 普通用户尝试删除其他用户数据 |
| 404 Not Found | 资源不存在 | 访问 /users/99999 但该 id 不存在 |
| 422 Unprocessable Entity | 数据校验失败(格式正确但语义错误) | 邮箱格式错误、年龄超出范围 |
3.4 5xx 服务器错误
| 状态码 | 含义 | 典型原因 |
|---|---|---|
| 500 Internal Server Error | 服务器内部错误 | 代码抛出未捕获异常、数据库崩溃 |
| 503 Service Unavailable | 服务不可用 | 过载、熔断、维护中 |
3.5 易混淆对比
| 对比 | 区分要点 |
|---|---|
| 400 vs 422 | 400:请求格式错误(如 JSON 解析失败);422:格式正确但字段值不符合业务规则 |
| 401 vs 403 | 401:未认证(未登录);403:已认证但无权限 |
| 307 vs 308 | 307 临时,308 永久;两者都保留请求方法(区别于 301/302) |
| 500 vs 503 | 500 内部错误(代码 bug);503 服务不可用(过载、维护) |
4. 子路由(模块化)
当项目变大时,将所有路由写在 main.py 会导致臃肿难维护。使用 APIRouter 拆分模块。
python
# routers/students.py
from fastapi import APIRouter
router = APIRouter(prefix="/students", tags=["学生管理"])
@router.get("/")
async def get_students():
return [{"name": "张三"}, {"name": "李四"}]
@router.post("/")
async def create_student():
return {"msg": "创建成功"}
在主应用中引入:
python
# main.py
from fastapi import FastAPI
from routers import students, teachers
app = FastAPI()
app.include_router(students.router)
app.include_router(teachers.router)
5. 依赖注入(Depends)
依赖注入用于抽离公共逻辑(如分页参数、认证、数据库会话)。FastAPI 的 Depends 会自动解析并注入。
python
from fastapi import Depends
def pagination_params(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
@app.get("/items")
async def get_items(params: dict = Depends(pagination_params)):
return params
依赖函数可以是普通函数或异步函数,甚至可以依赖其他依赖。
6. 中间件(Middleware)
中间件在每个请求前和响应后执行,可用于日志、耗时统计、权限校验、统一响应头等。
6.1 作用与实现结构
python
from fastapi import Request
import time
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request) # 继续处理请求
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
6.2 常见应用
- 耗时中间件:统计接口处理时间
- 认证中间件:校验 token,支持白名单路径
- 日志中间件:记录请求方法、路径、状态码
6.3 多中间件顺序
中间件按添加的顺序执行:请求时从外到内,响应时从内到外。例如:
python
app.add_middleware(MiddlewareA) # 先添加的外层
app.add_middleware(MiddlewareB) # 后添加的内层
7. 跨域(CORS)配置
前后端分离时,前端域名与后端不同会产生跨域问题。FastAPI 内置 CORSMiddleware。
python
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有来源(生产环境应指定具体域名)
allow_credentials=True, # 允许携带 Cookie
allow_methods=["*"], # 允许所有 HTTP 方法
allow_headers=["*"], # 允许所有请求头
)
8. 静态资源服务
使用 StaticFiles 挂载静态目录(如图片、CSS、JS)。
python
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")
访问 http://localhost:8000/static/logo.png 即可获取文件。
自定义缓存控制 :继承 StaticFiles 并重写 file_response 方法,添加 Cache-Control 头。
9. 综合实战:图书管理 API(完整代码)
下面结合 ORM、RESTful、子路由、依赖注入、中间件、CORS 等,实现完整的图书管理 API。
python
# models.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./books.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class Book(Base):
__tablename__ = "books"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
author = Column(String)
year = Column(Integer)
Base.metadata.create_all(bind=engine)
# crud.py (依赖数据库会话)
def get_book(db, book_id: int):
return db.query(Book).filter(Book.id == book_id).first()
def get_books(db, skip: int = 0, limit: int = 10):
return db.query(Book).offset(skip).limit(limit).all()
def create_book(db, title: str, author: str, year: int):
book = Book(title=title, author=author, year=year)
db.add(book)
db.commit()
db.refresh(book)
return book
def delete_book(db, book_id: int):
book = get_book(db, book_id)
if book:
db.delete(book)
db.commit()
return book
# dependencies.py
from fastapi import Depends
from .models import SessionLocal
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def pagination(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
# routers/books.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from .. import crud, dependencies
from ..models import Book
router = APIRouter(prefix="/books", tags=["图书管理"])
@router.get("/")
async def list_books(
pagination: dict = Depends(dependencies.pagination),
db: Session = Depends(dependencies.get_db)
):
books = crud.get_books(db, skip=pagination["skip"], limit=pagination["limit"])
return books
@router.post("/", status_code=201)
async def add_book(title: str, author: str, year: int, db: Session = Depends(dependencies.get_db)):
return crud.create_book(db, title, author, year)
@router.get("/{book_id}")
async def get_book(book_id: int, db: Session = Depends(dependencies.get_db)):
book = crud.get_book(db, book_id)
if not book:
raise HTTPException(status_code=404, detail="图书不存在")
return book
@router.delete("/{book_id}")
async def remove_book(book_id: int, db: Session = Depends(dependencies.get_db)):
book = crud.delete_book(db, book_id)
if not book:
raise HTTPException(status_code=404, detail="图书不存在")
return {"msg": "删除成功"}
# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from routers import books
app = FastAPI(title="图书管理API")
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 静态文件
app.mount("/static", StaticFiles(directory="static"), name="static")
# 子路由
app.include_router(books.router)
# 根路径
@app.get("/")
def root():
return {"message": "图书管理API已启动"}
启动后访问 /docs 即可测试所有接口。
10. 总结与系列回顾
本系列三篇文章涵盖了 FastAPI 从入门到实战的全路径:
| 篇目 | 核心内容 |
|---|---|
| 第一篇 | HTTP 协议、路由、参数、异常、异步、ASGI、Uvicorn |
| 第二篇 | Pydantic 模型、文件上传、表单、响应模型、自定义校验 |
| 第三篇(本文) | ORM、RESTful、状态码、子路由、依赖注入、中间件、CORS、静态文件 |
后续你可以:
- 将项目部署到云服务器(使用 Gunicorn + Uvicorn workers)
- 添加身份认证(JWT)
- 集成 Redis 做缓存
- 编写单元测试
- 使用 Docker 容器化
FastAPI 是现代 Python Web 开发的不二之选。希望这个系列能帮助你写出专业、高效的 API 服务。