什么是中间件
在构建 Web 应用时,我们常常需要处理诸如日志记录、请求验证、响应压缩等通用任务。如果每次都在每个路由函数中重复编写这些代码,不仅会导致代码冗余,还会使代码难以维护和扩展。FastAPI 中的中间件(Middleware
)机制为解决这些问题提供了优雅的方案,它可以在请求到达路由之前和响应返回给客户端之前,对请求和响应进行统一处理,极大地提升了 Web 应用的灵活性与效率
中间件就像是 Web 应用的 "门卫" 和 "质检员",它位于客户端请求与应用路由之间,以及应用路由与客户端响应之间。当客户端发送一个请求时,请求会按照顺序依次经过各个中间件,每个中间件可以对请求进行处理、修改或添加信息。处理完请求后,响应又会以相反的顺序再次经过中间件,中间件可以对响应进行调整,比如添加响应头、压缩响应体等。通过中间件,我们能够将一些通用的逻辑从路由函数中分离出来,实现代码的复用和模块化
为什么使用中间件
- 代码复用:将通用逻辑(如身份验证、日志记录)封装在中间件中,避免在多个路由函数中重复编写相同代码。
- 集中管理:所有中间件都在一个地方注册和配置,方便对这些通用功能进行统一管理和修改。
- 提高可维护性:当需要修改某个通用功能时,只需要在中间件中进行修改,而无需逐一修改每个路由函数。
- 增强扩展性:可以根据需求灵活添加或移除中间件,轻松扩展应用的功能。
FastAPI 中间件的基本使用
在 FastAPI 中使用中间件非常简单,只需要通过app.add_middleware()
方法将中间件添加到 FastAPI 应用实例中。下面是一个简单的示例,展示如何创建一个自定义中间件来记录请求的开始和结束时间:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import time
app = FastAPI()
# 自定义中间件
@app.middleware("http")
async def add_process_time_header(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
# 添加CORS中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def root():
return {"message": "Hello World"}
在上述代码中:
- 首先定义了一个 FastAPI 应用实例
app
- 使用
@app.middleware("http")
装饰器创建了一个自定义的 HTTP
中间件add_process_time_header
。在这个中间件中,记录了请求开始的时间,然后调用call_next(request)
将请求传递给下一个中间件或路由函数,获取响应后,计算请求处理的时间,并将其添加到响应头中 - 使用
app.add_middleware()
方法添加了一个CORSMiddleware
,用于处理跨域资源共享(CORS
)问题,允许所有来源、所有方法和所有头部的请求 - 最后定义了一个根路由/,当访问该路由时,返回一个包含
"Hello World"
消息的 JSON 响应
常用的 FastAPI 中间件
CORS 中间件
在现代 Web
开发中,由于浏览器的同源策略限制,当 Web
应用从不同的域名、端口或协议请求资源时,会出现跨域问题。FastAPI 的CORSMiddleware
可以轻松解决这个问题。通过配置allow_origins
、allow_methods
、allow_headers
等参数,可以精确控制哪些来源、方法和头部的请求被允许
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com"], # 允许的来源
allow_credentials=True,
allow_methods=["GET", "POST"], # 允许的HTTP方法
allow_headers=["Content-Type"], # 允许的请求头部
)
GZip 中间件
GZip 中间件用于对响应进行压缩,减少数据传输量,提高应用的性能。特别是对于传输较大数据的 API 接口,使用 GZip 压缩可以显著加快响应速度
from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(
GZipMiddleware,
minimum_size=1000 # 只有当响应体大小超过1000字节时才进行压缩
)
身份验证中间件
在实际应用中,很多接口需要进行身份验证,确保只有合法用户才能访问。可以创建一个自定义的身份验证中间件,在请求到达路由之前验证用户的身份信息。例如,验证请求头中的 JWT 令牌:
from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
import jwt
# 自定义身份验证中间件
@app.middleware("http")
async def authenticate(request: Request, call_next):
token = request.headers.get("Authorization")
if not token:
raise HTTPException(status_code=401, detail="Unauthorized")
try:
payload = jwt.decode(token.split(" ")[1], "secret_key", algorithms=["HS256"])
request.state.user = payload # 将用户信息存储在请求对象中,供路由函数使用
except jwt.PyJWTError:
raise HTTPException(status_code=401, detail="Invalid token")
response = await call_next(request)
return response
中间件的执行顺序
中间件的执行顺序非常重要,因为请求和响应会按照中间件注册的顺序依次经过它们。当添加中间件时,先注册的中间件会先处理请求,后注册的中间件后处理请求;而在响应阶段,顺序则相反,后注册的中间件先处理响应,先注册的中间件后处理响应。因此,在设计中间件时,需要根据功能的依赖关系合理安排中间件的注册顺序