FastAPI的中间件与路由 -- pd的FastAPI笔记
文章目录
-
- [FastAPI的中间件与路由 -- pd的FastAPI笔记](#FastAPI的中间件与路由 -- pd的FastAPI笔记)
- 中间件基本结构
-
- 中间件注册机制
-
- [方式一:使用 `@app.middleware("http")` 装饰器](#方式一:使用
@app.middleware("http")装饰器) - [方式二:使用 `app.add_middleware()` 方法](#方式二:使用
app.add_middleware()方法) - [🔁 执行流程对比](#🔁 执行流程对比)
- [方式一:使用 `@app.middleware("http")` 装饰器](#方式一:使用
- [⚠️ 注意事项](#⚠️ 注意事项)
- FastAPI跨域配置
- FastAPI路由划分
- FastAPI项目结构
中间件基本结构
中间件是位于客户端和应用程序核心逻辑之间的软件层,用于:
- 拦截请求和响应
- 在请求到达路由处理程序之前进行处理
- 在响应返回给客户端之前进行处理

python
from fastapi import FastAPI
import time
app = FastAPI(debug=True)
@app.middleware("http")
async def custom_middleware(request, call_next):
# 响应请求之前执行
start_time = time.time()
print(f'{start_time} custom_middleware called')
response = await call_next(request)
# 响应请求之后执行
end_time = time.time()
process_time = end_time - start_time
print(f'{end_time} custom_middleware after called')
response.headers["X-Process-Time"] = str(process_time)
return response
@app.get('/user')
async def user():
print(f'{time.time()} user called')
return 'user'
if __name__ == '__main__':
import uvicorn
uvicorn.run("18中间件:app", host='127.0.0.1',
port=8003, reload=True)
关键组件
- request: Request :传入的请求对象
- call_next :调用链中的下一个中间件/路由
- 返回值:必须返回
中间件的执行顺序
- 先注册的中间件在内层,后注册的中间件在外层。
请求阶段(从外到内)
- 客户端发送 HTTP 请求。
- 请求首先经过 最后注册的中间件(外层)。
- 每个中间件调用 call_next(request) 后,请求进入 上一层中间件。
- 最终请求到达 最先注册的中间件(内层),然后交给路由处理程序。
响应阶段(从内到外)
- 路由处理程序生成响应。
- 响应从 最先注册的中间件(内层)开始返回。
- 每个中间件在 call_next(request) 之后执行后置逻辑。
- 响应最终通过 最后注册的中间件(外层)返回给客户端。
中间件注册机制
在 FastAPI 中,除了使用 @app.middleware("http") 装饰器注册中间件外,还可以通过 app.add_middleware() 方法显式加载中间件。这两种方式的执行顺序规则是一致的:先注册的中间件在内层,后注册的中间件在外层。
方式一:使用 @app.middleware("http") 装饰器
python
from fastapi import FastAPI
import time
app = FastAPI(debug=True)
# 第一个注册的中间件(内层)
@app.middleware("http")
async def middleware_1(request, call_next):
print("Middleware 1: 请求开始")
response = await call_next(request)
print("Middleware 1: 响应结束")
return response
# 第二个注册的中间件(外层)
@app.middleware("http")
async def middleware_2(request, call_next):
print("Middleware 2: 请求开始")
response = await call_next(request)
print("Middleware 2: 响应结束")
return response
@app.get("/user")
async def user():
print("Route Handler: 处理请求")
return {"message": "Hello, User!"}
方式二:使用 app.add_middleware() 方法
python
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
import time
app = FastAPI(debug=True)
# 定义中间件类
class Middleware1(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
print("Middleware 1: 请求开始")
response = await call_next(request)
print("Middleware 1: 响应结束")
return response
class Middleware2(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
print("Middleware 2: 请求开始")
response = await call_next(request)
print("Middleware 2: 响应结束")
return response
# 注册中间件(先注册的在内层,后注册的在外层)
app.add_middleware(Middleware1) # 内层
app.add_middleware(Middleware2) # 外层
@app.get("/user")
async def user():
print("Route Handler: 处理请求")
return {"message": "Hello, User!"}
🔁 执行流程对比
两种方式的执行顺序完全一致:
客户端发送请求
↓
Middleware 2: 请求开始(外层)
↓
Middleware 1: 请求开始(内层)
↓
Route Handler: 处理请求
↓
Middleware 1: 响应结束(内层)
↓
Middleware 2: 响应结束(外层)
↓
客户端接收响应
⚠️ 注意事项
-
混合使用时的顺序:
-
如果同时使用
@app.middleware("http")和app.add_middleware(),装饰器注册的中间件会排在add_middleware()注册的中间件之后。 -
例如:
pythonapp.add_middleware(Middleware1) # 内层 app.add_middleware(Middleware2) # 外层 @app.middleware("http") async def middleware_3(request, call_next): print("Middleware 3: 请求开始") response = await call_next(request) print("Middleware 3: 响应结束") return response执行顺序为:
Middleware 3(最外层)→Middleware 2→Middleware 1(最内层)。
-
-
中间件类 vs 装饰器:
BaseHTTPMiddleware类更适合封装复杂逻辑。- 装饰器方式更简洁,适合轻量级中间件。
FastAPI跨域配置
同源策略(SOP)
同源策略是浏览器的一种安全机制,限制了一个源(origin)的网页如何与另一个源的资源进行交互。
- 源(origin)由协议(如HTTP/HTTPS)、域名(如example.com)和端口(如80或443)组成。
- 例如,https://example.com:443 和 http://example.com:80 是不同源,因为协议和端口不同。
同源策略防止恶意网站通过脚本(如JavaScript)未经授权访问其他网站的数据,例如窃取用户的敏感信息
但是:现代Web应用经常需要跨源请求!
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种基于HTTP的机制,它允许服务器指示哪些其他源(域名、协议或端口)可以访问其资源,从而绕过浏览器的同源策略(Same-Origin Policy,SOP)限制
使用官方提供的CORS中间件
python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(debug=True)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 或者指定前端域名'http://localhost:8080'
allow_credentials=True, # 允许cookie
allow_methods=["*"], # 允许的请求方法
allow_headers=["*"], # 允许的请求头
)
@app.get("/info")
async def info():
return {"message": "Hello World"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("19CORS中间件:app", host='127.0.0.1',
port=8003, reload=True)
FastAPI路由划分
APIRouter 核心作用
- 模块化架构:将大型应用拆分为独立功能模块
- 路由分组:统一管理相关端点
- 组织优化:解耦业务逻辑,提升可维护性
python
from fastapi import APIRouter
app = FastAPI()
main_router = APIRouter(perfix="/api/v1")
user_router = APIRouter(tags=["用户应用"], perfix="/users")
item_router = APIRouter(tags=["商品应用"], perfix="/items")
# 将子路由器添加到主路由器
main_router.include_router(user_router)
main_router.include_router(item_router)
# 将主路由器添加到FastAPI实例中
app.include_router(main_router)
@user_router.get("/profile")
async def get_profile():
pass
@item_router.get("/{item_id}")
async def get_item(item_id):
pass
FastAPI项目结构
FastAPI 项目中,代码全部集中在单个文件中,虽然这种方式适合小型项目或原型开发,但在企业级应用中,将代码拆分为多个文件和目录是更优的实践,主要原因:
- 职责分离:将应用的各个组成部分(如数据库配置、模型、模式、路由、中间件)分别放置在单独的文件或目录中,使代码结构更清晰,便于理解和维护
- 提高代码可读性和可维护性:将代码拆分为小而专一的文件,开发者可以快速定位和修改特定功能,而无需在单一长文件中搜索
- 增强可扩展性:模块化的结构便于扩展,当需要添加新功能(如新的模型或路由)时,只需在相应的目录中创建新文件,而不影响现有代码
- 清晰的依赖管理:通过在每个文件中显式导入依赖,可以清楚地看到模块之间的关系,便于调试和理解代码逻辑
- 便于团队协作:在企业开发中,多个开发者可能同时开发不同功能,拆分后的文件结构允许团队成员并行工作。例如,一个开发者修改用户路由,另一个开发者可以同时处理商品路由,互不干扰。
- 符合企业级项目规范:企业级项目通常需要遵循标准化的代码组织规范,拆分文件是行
业最佳实践之一。这种结构也便于集成自动化测试、CI/CD 流程和代码审查。
按软件功能目录结构示例
py
project/
├── main.py # 主入口文件,初始化 FastAPI 应用并集成路由、中间件
├── config/
│ └── database.py # 数据库配置和 Tortoise-ORM 初始化
├── models/
│ └── user.py # 数据模型定义(如 User 模型)
├── schemas/
│ └── user.py # Pydantic 模式定义,用于输入输出验证
├── routers/
│ ├── __init__.py # 标记 routers 为 Python 包
│ ├── user.py # 用户相关的 API 路由
│ └── item.py # 商品相关的 API 路由
└── middleware/
└── user_middleware.py # 自定义中间件逻辑
按业务模块目录结构示例
py
project/
├── main.py # 主入口文件,初始化 FastAPI 应用并集成路由、中间件
├── config/
│ └── database.py # 数据库配置和 Tortoise-ORM 初始化
├── modules/
│ ├── user/
│ │ ├── __init__.py # 模块入口文件,标记 user 为包
│ │ ├── models.py # 用户模型定义
│ │ ├── schemas.py # 用户数据结构定义
│ │ └── routes.py # 用户路由定义
│ │── item/
│ │ ├── __init__.py # 模块入口文件,标记 item 为包
│ │ ├── models.py # 商品模型定义
│ │ ├── schemas.py # 商品数据结构定义
│ │ └── routes.py # 商品路由定义
└── middleware/
└── user_middleware.py # 自定义中间件逻辑
其他类型的项目结构
例如采取标准FastAPI架构的设计:
┌─────────────────────────────────────────────────────────────┐
│ API 路由层 (api/) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ FastAPI 路由定义 │ 请求验证 │ 响应格式化 │ 文档生成 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 业务逻辑层 (services/) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 业务规则处理 │ 事务管理 │ 错误处理 │ 数据转换 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 数据访问层 (repositories/) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 数据库操作封装 │ 查询优化 │ 缓存管理 │ 事务控制 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 数据源层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MySQL 数据库 │ Redis 缓存 │ 外部 API 服务 │ 文件存储 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
py
project/
├── src/ # 源代码目录
│ └── project_name_api/ # 主应用包
│ ├── api/ # API 路由层
│ │ └── endpoints/ # 各功能模块的API端点
│ │ ├── files.py # 文件服务API
│ │ ├── ...
│ │ └── users.py # 用户服务API
│ ├── core/ # 核心组件层
│ │ ├── config.py # 配置管理
│ │ ├── database.py # 数据库连接
│ │ ├── ...
│ │ ├── models.py # 数据模型
│ │ └── redis.py # Redis连接
│ ├── integrations/ # 外部服务集成层
│ │ ├── llm_api.py # 大模型接口
│ │ └── ...
│ ├── middleware/ # 中间件层
│ │ ├── logging.py # 日志中间件
│ │ └── ...
│ ├── repositories/ # 数据访问层
│ │ ├── file_repo.py # 文件服务的CRUD操作
│ │ └── user_repo.py # 用户服务的CRUD操作
│ ├── schemas/ # Pydantic 数据模型
│ │ ├── file_schema.py # 文件服务的数据验证模型
│ │ └── user_schema.py # 用户服务的数据验证模型
│ ├── services/ # 业务逻辑层
│ │ ├── file_service.py # 文件服务的业务逻辑
│ │ └── user_service.py # 用户服务的业务逻辑
│ ├── scheduler/ # 定时任务调度
│ │ └── scheduler_email.py # 定时邮件任务
│ ├── utils/ # 工具函数
│ └── main.py # 应用入口点
├── lib/ # 本地依赖包
│ ├── service_a-0.1.0.tar.gz # 服务A(身份认证相关)
│ └── sdk_b-0.1.1.tar.gz # SDK B(文档处理相关)
├── sql/ # 数据库脚本
│ ├── schema_full.sql # 完整表结构
│ ├── seed_data.sql # 初始数据
│ └── migration_v1.sql # 表结构变更
├── scripts/ # 脚本文件
│ └── utility_script.py # 工具脚本示例
├── tests/ # 测试代码
├── config.yaml # 应用配置文件
└── pyproject.toml # 项目配置和依赖管理