文章目录
- [一、 中间件(Middleware):全局的"拦截器"](#一、 中间件(Middleware):全局的“拦截器”)
-
- [1.1 核心概念](#1.1 核心概念)
- [1.2 执行原理](#1.2 执行原理)
- [1.3 代码实现](#1.3 代码实现)
- [1.4 多中间件执行顺序](#1.4 多中间件执行顺序)
- [二、 依赖系统(Dependency Injection):精细化的"业务注入"](#二、 依赖系统(Dependency Injection):精细化的“业务注入”)
-
- [2.1 为什么要用依赖系统?](#2.1 为什么要用依赖系统?)
- [2.2 如何实现依赖?](#2.2 如何实现依赖?)
- 三、如何实现"局部中间件"?
- [四、 注意注意注意注意注意](#四、 注意注意注意注意注意)
一、 中间件(Middleware):全局的"拦截器"
1.1 核心概念
中间件是一个在每个 请求 (Request) 被处理之前,以及每个 响应 (Response) 返回之前运行的函数。它是全局性的,一旦建立,项目中的所有路由都会经过它。
1.2 执行原理
中间件通过 call_next 建立起请求的传递链:
1、前置处理:请求向下传递(交给路由)之前执行。
2、传递请求:调用 await call_next(request) 。
3、后置处理:路由处理完后,在响应返回给客户端之前执行。
1.3 代码实现
在 main.py 中直接定义(注意:APIRouter 不支持直接定义中间件):
python
@app.middleware("http")
async def process_timer(request: Request, call_next):
# 【前置】记录请求开始时间
print("中间件:开始拦截请求")
response = await call_next(request)
# 【后置】添加自定义响应头或日志
print("中间件:准备返回结果")
return response
1.4 多中间件执行顺序
当有多个中间件时,执行顺序是从下向上(或者说后定义的先执行请求,先定义的后处理响应)。
二、 依赖系统(Dependency Injection):精细化的"业务注入"
2.1 为什么要用依赖系统?
如果说中间件是"强迫所有人过安检",那么依赖系统就是"谁需要谁申请"。
假设你写了两个接口:一个是"查看个人资料",一个是"修改个人资料"。这两个接口都需要先验证用户是否登录。
这时候,你写一个验证函数作为"依赖项",谁需要验证,谁就在参数里"领取"它。
2.2 如何实现依赖?
python
# 1. 定义一个依赖项
async def verify_token(token: str):
if token != "secret-key":
return {"error": "暗号不对,不准入内"}
return "验证通过"
# 2. 在需要的路由里"领取"它
@user_router.get("/info/")
async def get_user_info(auth_status = Depends(verify_token)):
# 只有 verify_token 成功了,才会执行这里
return {"status": auth_status, "name": "Chang Yang"}
三、如何实现"局部中间件"?
中间件一旦建立,所有路由都得走,能不能让某些路由走,某些不走?
答案是:当然能!!!
在 FastAPI 里,这通常有两种实现方案:
- Plan A:在中间件内部做排除(白名单)
在中间件函数里加个判断:
python
@app.middleware("http")
async def selective_middleware(request: Request, call_next):
# 定义不需要走中间件的路径
white_list = ["/login", "/register", "/docs"]
if request.url.path in white_list:
return await call_next(request) # 直接放行,不走下面的逻辑
# 下面是正常的拦截逻辑...
print("正在拦截受保护的路径")
return await call_next(request)
- Plan B:使用"依赖注入"模拟局部中间件
部分代码示例(这里是一个单独的py文件):
python
import time
# 用依赖系统上实现局部中间件
# 第一步:定义一个依赖函数
async def my_timer_dep():
start_time = time.time()
yield
process_time = time.time() - start_time
print("接口耗时:", process_time, "秒")
python
from fastapi import Depends
# 导入依赖函数
from aps.Common.deps import my_timer_dep
@api_router.get('/{id}',dependencies=[Depends(my_timer_dep)])
async def get_user(id: int):
return {
'id': id,
'name': '张三'
}
四、 注意注意注意注意注意
- 路径斜杠问题:在定义带有查询参数的路径时,结尾建议加上 /。虽然某些 AI 或文档说不加也能跑,但在标准 RESTful 实践和 FastAPI 的某些配置下,加斜杠能避免不必要的重定向问题。
- 异步避坑:如果你在中间件里处理文件或耗时 I/O,一定要记得使用 await,否则会阻塞整个异步循环。