介绍
FastAPI中间件本质是 ASGI中间件,在请求到达路由函数之前、响应返回客户端之前做拦截处理,可以处理请求、修改请求、修改响应、异常捕获、日志、计时、跨域等通用逻辑。
FastAPI基于Starlette,中间件完全复用Starlette中间件机制
有两种写法:
- 类中间件(传统ASGI)
@app.middleware("http")装饰器中间件(最常用)
理解
对于java程序员,可以理解为过滤器、拦截器
应用场景
全局请求日志打印:打印请求方法、url、客户端 IP、请求头、状态码、响应耗时;统一访问日志。
请求限流、IP 黑名单:拦截黑名单 IP;简单全局计数器限流,超限直接返回 429。
GZIP、压缩、代理转发相关:简单响应内容修改;复杂压缩优先使用内置 GZipMiddleware。
CORS 跨域:官方内置 CORSMiddleware,处理跨域预检 OPTIONS 请求。
统一修改请求header、响应 header、状态码。
不适合
- 鉴权:一般只有部分接口需要鉴权
执行流程
- 客户端发起HTTP请求 → 中间件接收请求(传入
request对象) - 中间件预处理请求(如:修改header、日志、计时开始)
- 将请求向下传递,交给路由处理
- 路由执行视图函数,返回
response响应对象 - 回到中间件,对
response做后置处理(添加响应头、计算耗时) - 返回
response给客户端
实现方式1-装饰器(推荐)
普通函数上面加上装饰器:@app.middleware("http"),http 表示拦截 HTTP 请求
关键点:call_next(request) 是把请求交给后续链路,必须调用,否则请求不会走到接口;它返回的是response。
例子
from fastapi import FastAPI
app = FastAPI()
@app.middleware("http")
async def my_middleware1(request, call_next):
print(f"中间件1-收到请求: {request.method}请求方法,URL:{request.url}")
# 把请求交给下一层,执行接口,拿到响应,必须写await
response = await call_next(request)
print(f"中间件1-请求结束")
return response
@app.get('/student/{name}')
async def get_student(name: str):
return {
"code": 200,
"data": {
'name': name,
'age': 21,
}
}
测试
浏览器发请求:http://127.0.0.1:8000/student/李雷 ,控制台打印如下:
中间件1-收到请求: GET请求方法,URL:http://127.0.0.1:8000/student/李雷
执行路由处理函数
中间件1-请求结束
实现方式2-类实现
定义类,继承 BaseHTTPMiddleware 父类,只处理 http 请求
好处:可以封装为独立类,多个项目复用。
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
app = FastAPI()
class MyMiddleware1(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next) -> Response:
print(f"中间件1-收到请求: {request.method}请求方法,URL:{request.url}")
resp = await call_next(request)
print(f"中间件1-请求结束")
return resp
# 注册中间件
app.add_middleware(MyMiddleware1)
@app.get('/student/{name}')
async def get_student(name: str):
print("执行路由处理函数")
return {
"code": 200,
"data": {
'name': name,
'age': 21,
}
}
测试
浏览器发请求:http://127.0.0.1:8000/student/李雷 ,控制台打印如下:
中间件1-收到请求: GET请求方法,URL:http://127.0.0.1:8000/student/李雷
执行路由处理函数
中间件1-请求结束
多个中间件执行顺序-装饰器实现
装饰器 @app.middleware("http") 执行顺序:从下到上
例子
from fastapi import FastAPI
app = FastAPI()
@app.middleware("http")
async def my_middleware1(request, call_next):
print(f"中间件1-收到请求: {request.method}请求方法,URL:{request.url}")
# 把请求交给下一层,执行接口,拿到响应,必须写await
response = await call_next(request)
print(f"中间件1-请求结束")
return response
@app.middleware("http")
async def my_middleware2(request, call_next):
print(f"中间件2-收到请求: {request.method}请求方法,URL:{request.url}")
# 把请求交给下一层,执行接口,拿到响应,必须写await
response = await call_next(request)
print(f"中间件2-请求结束")
return response
@app.get('/student/{name}')
async def get_student(name: str):
print("执行路由处理函数")
return {
"code": 200,
"data": {
'name': name,
'age': 21,
}
}
测试
浏览器发请求:http://127.0.0.1:8000/student/李雷 ,控制台打印如下:
中间件2-收到请求: GET请求方法,URL:http://127.0.0.1:8000/student/李雷
中间件1-收到请求: GET请求方法,URL:http://127.0.0.1:8000/student/李雷
执行路由处理函数
中间件1-请求结束
中间件2-请求结束
从下到上:先执行下面的中间件,再执行上面的中间件
多个中间件执行顺序-类实现
add_middleware() 执行顺序:从下到上,即:后添加的先执行
例子
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
app = FastAPI()
class MyMiddleware1(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next) -> Response:
print(f"中间件1-收到请求: {request.method}请求方法,URL:{request.url}")
resp = await call_next(request)
print(f"中间件1-请求结束")
return resp
class MyMiddleware2(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next) -> Response:
print(f"中间件2-收到请求: {request.method}请求方法,URL:{request.url}")
resp = await call_next(request)
print(f"中间件2-请求结束")
return resp
# 注册中间件
app.add_middleware(MyMiddleware1)
app.add_middleware(MyMiddleware2)
@app.get('/student/{name}')
async def get_student(name: str):
print("执行路由处理函数")
return {
"code": 200,
"data": {
'name': name,
'age': 21,
}
}
测试
浏览器发请求:http://127.0.0.1:8000/student/李雷 ,控制台打印如下:
中间件2-收到请求: GET请求方法,URL:http://127.0.0.1:8000/student/李雷
中间件1-收到请求: GET请求方法,URL:http://127.0.0.1:8000/student/李雷
执行路由处理函数
中间件1-请求结束
中间件2-请求结束
从下到上,即:后添加的先执行
常见内置中间件
1. CORS跨域中间件
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许的源,生产环境不要*
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
2. GZip压缩中间件
from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(GZipMiddleware, minimum_size=1000)
大于1000字节响应自动gzip压缩。
直接返回响应(拦截请求,不走路由)
不调用 call_next,直接返回 Response,请求不会进入路由函数,适合鉴权拦截
注意:一般不用中间件鉴权,而是使用依赖
from fastapi.responses import JSONResponse
@app.middleware("http")
async def auth_mw(request, call_next):
token = request.headers.get("token")
if not token:
# 直接返回,不走接口
return JSONResponse(status_code=401, content={"code":401,"msg":"未登录"})
return await call_next(request)
