(十八)用应用工厂与 lifespan 组合项目
1. 在应用启动和结束时管理共享资源
路由都已经登记在各自的 router 上,最后由应用工厂组合。工厂让真实服务、临时测试库和演示使用同样的应用代码,只替换配置。
# 项目文件:api_project/src/bookapi/app.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from .config import Settings
from .database import open_database
from .errors import ErrorBody, install_handlers
from .routes import auth, books, exports
def create_app(settings: Settings | None = None) -> FastAPI:
settings = settings or Settings()
@asynccontextmanager
async def lifespan(app: FastAPI):
engine, factory = open_database(settings)
app.state.settings = settings
app.state.session_factory = factory
try:
yield # Uvicorn 在此期间接收请求。
finally:
engine.dispose() # 停止应用时释放连接池。
app = FastAPI(
title="个人书籍 API",
version="1.0.0",
lifespan=lifespan,
responses={
code: {"model": ErrorBody} for code in [401, 403, 404, 409, 422, 500]
},
)
install_handlers(app)
for router in [auth.router, books.router, exports.router]:
app.include_router(router)
return appcreate_app() 返回一个新的 FastAPI 应用。include_router 将每组路由并入应用,最终路径包含各自的 prefix。路由文件通过相对导入复用模型、服务和依赖,知识关系可以直接沿导入查找。
responses 为常见错误状态声明 ErrorBody 模型,OpenAPI 中的 422 因而使用本项目的 error 结构。这项声明负责文档,实际错误仍由前面的异常处理器生成。
第五章用 contextlib.contextmanager 把生成器变成同步上下文管理器。这里导入的 asynccontextmanager 是它的异步版本,包装一个 async def 定义、含有一次 yield 的异步生成器。进入 async with 时运行到 yield,退出时从那里恢复并执行收尾;进入和退出过程中还可以用 await 等待资源操作。
FastAPI 按这种方式管理 lifespan:在接收请求前准备数据库,将配置和会话工厂放入 app.state;在 yield 处进入服务运行阶段;服务结束后恢复执行,释放连接池。这里使用同步数据库,所以准备和释放没有额外的 await。导入 bookapi 或 app.py 本身不会打开数据文件。
app.state.session_factory 是共享工厂,get_session 调用它才创建本次请求的工作对象。把一份 Session 直接放进 app.state,多个请求就会同时碰到同一组事务与对象状态;共享工厂则让创建出来的会话彼此独立。
生命周期接口可对照 FastAPI lifespan。数据库结构调整继续通过迁移完成;本项目第一次启动创建表,后续启动读取同一文件。
2. 目录中的职责连接
| 文件 | 负责的工作 |
|---|---|
schemas.py |
输入、公开输出与字段规则 |
models.py |
表结构、主键、外键和唯一约束 |
database.py、config.py |
数据库位置、引擎与会话工厂 |
dependencies.py、security.py |
本次会话、令牌检查与当前身份 |
service.py |
归属、筛选与 CRUD 事务 |
routes/ |
HTTP 参数、状态、响应头和动作连接 |
errors.py |
事务工具与错误响应 |
app.py、cli.py |
应用组合、生命周期、初始化和服务入口 |
jobs.py |
响应后的独立数据库任务 |