当前位置: 首页 > news >正文

FastAPI 中间件

FastAPI 中间件学习笔记

一、中间件概念

中间件是一个在请求到达路由处理函数之前响应返回客户端之前执行的函数,类似于一个"拦截器":

客户端请求 → 中间件1 → 中间件2 → ... → 路由处理函数 ↓ 客户端响应 ← 中间件1 ← 中间件2 ← ... ← 路由处理函数返回

核心作用:在请求/响应的流转过程中插入通用逻辑,如日志、认证、CORS、计时等。


二、基本用法 —@app.middleware

1. 最简示例

fromfastapiimportFastAPI,Request app=FastAPI()@app.middleware("http")asyncdefsimple_middleware(request:Request,call_next):# ---- 请求阶段(before route handler)----print(f"请求进入:{request.method}{request.url}")# ---- 调用下一个中间件或路由处理函数 ----response=awaitcall_next(request)# ---- 响应阶段(after route handler)----print(f"响应状态:{response.status_code}")returnresponse

2. 执行流程

@app.middleware("http") async def my_middleware(request, call_next): # ① 请求前逻辑 response = await call_next(request) # ② 交给下一层 # ③ 响应后逻辑 return response # ④ 返回响应
阶段位置典型用途
① 请求前call_next之前认证、限流、注入上下文
② 传递call_next(request)调用下一个中间件/路由
③ 响应后call_next之后添加响应头、日志记录
④ 返回return response将处理后的响应返回

三、实用示例

1. 请求计时中间件

importtime@app.middleware("http")asyncdefadd_process_time_header(request:Request,call_next):start_time=time.perf_counter()response=awaitcall_next(request)process_time=time.perf_counter()-start_time response.headers["X-Process-Time"]=str(process_time)returnresponse

2. 认证中间件

fromfastapiimportRequest,HTTPExceptionfromstarlette.responsesimportJSONResponse@app.middleware("http")asyncdefauth_middleware(request:Request,call_next):# 白名单路径跳过认证ifrequest.url.pathin("/docs","/openapi.json","/login"):returnawaitcall_next(request)token=request.headers.get("Authorization")ifnottokenornotverify_token(token):returnJSONResponse(status_code=401,content={"detail":"Not authenticated"},)# 认证通过,继续处理response=awaitcall_next(request)returnresponsedefverify_token(token:str)->bool:# 实际项目中验证 JWT / Sessionreturntoken=="Bearer valid-token"

3. 请求日志中间件

importloggingimporttime logger=logging.getLogger("api.access")@app.middleware("http")asyncdeflogging_middleware(request:Request,call_next):start=time.perf_counter()response=awaitcall_next(request)duration=time.perf_counter()-start logger.info(f"{request.method}{request.url.path}"f"status={response.status_code}"f"duration={duration:.3f}s "f"client={request.client.hostifrequest.clientelse'unknown'}")returnresponse

4. 请求 ID 追踪中间件

importuuid@app.middleware("http")asyncdefrequest_id_middleware(request:Request,call_next):request_id=request.headers.get("X-Request-ID")orstr(uuid.uuid4())response=awaitcall_next(request)response.headers["X-Request-ID"]=request_idreturnresponse

四、中间件执行顺序

中间件按注册顺序执行请求阶段,按逆序执行响应阶段(洋葱模型):

@app.middleware("http")asyncdefmiddleware_a(request,call_next):print("A 请求前")response=awaitcall_next(request)print("A 响应后")returnresponse@app.middleware("http")asyncdefmiddleware_b(request,call_next):print("B 请求前")response=awaitcall_next(request)print("B 响应后")returnresponse

输出顺序

A 请求前 → B 请求前 → [路由处理] → B 响应后 → A 响应后
┌─── middleware_a ───┐ │ ┌── middleware_b ┐ │ │ │ [route handler] │ │ │ └─────────────────┘ │ └──────────────────────┘

注意@app.middleware装饰器注册的中间件,后注册的先执行请求阶段(栈结构)。实际执行顺序与代码书写顺序相反,需留意。


五、call_next详解

1. 基本行为

response=awaitcall_next(request)
  • 调用call_next将请求传递给下一层,返回Response对象。
  • 必须调用,否则请求不会到达路由处理函数。

2. 修改请求体

call_next接收的是Request对象,如需修改请求体,需构造新的Request

importjson@app.middleware("http")asyncdefmodify_body_middleware(request:Request,call_next):# 读取原始请求体body=awaitrequest.body()# 修改后构造新请求modified_body=json.dumps({"injected":True,**json.loads(body)}).encode()new_request=Request(request.scope,receive=lambda:receive_with_body(modified_body),)response=awaitcall_next(new_request)returnresponse

修改请求体场景较少见,通常建议用依赖注入(Depends)替代。

3. 提前返回响应

call_next之前直接返回响应,请求不会到达路由:

@app.middleware("http")asyncdefrate_limit_middleware(request:Request,call_next):ifis_rate_limited(request):returnJSONResponse(status_code=429,content={"detail":"Too many requests"})returnawaitcall_next(request)

六、内置中间件 — CORS

FastAPI(Starlette)提供了多个内置中间件,最常用的是 CORS:

fromfastapi.middleware.corsimportCORSMiddleware app.add_middleware(CORSMiddleware,allow_origins=["https://example.com","http://localhost:3000"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"],)
参数说明
allow_origins允许的源列表,["*"]表示全部
allow_methods允许的 HTTP 方法
allow_headers允许的请求头
allow_credentials是否允许携带 Cookie
max_age预检请求缓存时间(秒)

七、其他内置中间件

中间件用途示例
CORSMiddleware跨域资源共享见上方
HTTPSRedirectMiddleware强制 HTTPSapp.add_middleware(HTTPSRedirectMiddleware)
TrustedHostMiddleware主机白名单防止 Host 头攻击
GZipMiddleware响应压缩减少传输体积
SessionMiddlewareCookie-based Session基于 Cookie 的会话管理
fromstarlette.middleware.httpsredirectimportHTTPSRedirectMiddlewarefromstarlette.middleware.trustedhostimportTrustedHostMiddlewarefromstarlette.middleware.gzipimportGZipMiddleware app.add_middleware(GZipMiddleware,minimum_size=1000)app.add_middleware(TrustedHostMiddleware,allowed_hosts=["example.com","*.example.com"])app.add_middleware(HTTPSRedirectMiddleware)

add_middleware注册顺序:与@app.middleware相反,add_middleware先添加的在外层。建议将 CORS 放在最外层。


八、中间件 vs 依赖注入

对比项中间件依赖注入(Depends
作用范围全局,所有请求可按路由/路径选择性应用
执行时机路由匹配之前路由匹配之后
能否修改响应可以不方便
能否提前返回可以可以(抛 HTTPException)
典型场景日志、CORS、压缩、全局认证参数校验、数据库会话、权限检查
可读性集中管理声明式,与路由绑定

选择建议

  • 全局通用逻辑→ 中间件(如日志、CORS、压缩)
  • 路由级逻辑→ 依赖注入(如数据库连接、特定权限校验)

九、纯 ASGI 中间件

除了 FastAPI 的@app.middleware,还可以编写纯 ASGI 中间件,兼容任何 ASGI 框架:

classCustomASGIMiddleware:def__init__(self,app):self.app=appasyncdef__call__(self,scope,receive,send):ifscope["type"]=="http":# 请求前逻辑print("ASGI middleware: before request")awaitself.app(scope,receive,send)ifscope["type"]=="http":# 请求后逻辑print("ASGI middleware: after request")# 注册app.add_middleware(CustomASGIMiddleware)

纯 ASGI 中间件更底层、更灵活,但编写复杂。大多数场景用@app.middleware即可。


十、注意事项

  1. 中间件中不要抛 HTTPException:中间件中raise HTTPException不会被异常处理器捕获(因为异常处理器在路由层之后)。应直接返回JSONResponse
  2. 避免在中间件中读取请求体await request.body()会消费请求体流,后续路由无法再次读取。如必须读取,需重新构造Request
  3. 中间件顺序很重要:CORS 通常放最外层,认证放内层,日志放最外层。
  4. 性能考量:每个请求都会经过所有中间件,中间件中的阻塞操作会拖慢全局响应。
  5. 测试时注意:中间件会影响测试结果,TestClient会完整执行中间件链。
http://www.jsqmd.com/news/773034/

相关文章:

  • 实战指南:如何用特斯拉Model 3/Y的DBC文件构建智能车辆监控系统
  • AI Agent 入门课:RAG 不是检索外挂,而是 Agent 的知识闭环
  • 赵明能享受到千里科技的推背感吗?
  • 为什么 docker build 成功但 docker-compose build 失败?
  • 别再手动查了!教你用Python写个脚本,批量查询商品条码并自动保存信息到Excel
  • 3分钟掌握鸣潮120FPS解锁:WaveTools工具箱终极使用指南
  • 自建代理池实战:从零搭建高可用IP代理服务应对反爬策略
  • 2026年吹塑机厂家选购全解析:从选型困惑到解决方案 - 速递信息
  • 眨眼猫会务智能体:3位数报名签到查座会务小程序,到底能赚钱吗?
  • 从“零散台账”到“一屏掌控”:绿虫光伏全流程管理软件打通项目全链路
  • 天龙八部GM工具完整指南:3分钟掌握游戏数据管理的终极技巧
  • 告别Intent跳转!用ARouter重构你的Android模块化项目(附完整Gradle配置)
  • AISMM模型核心指标全拆解,从L1到L5逐级验证,你的企业卡在哪一级?
  • AISMM模型实战手册:3个月实现IT服务管理能力跃升,中小企CIO都在偷偷用的方法
  • 汽车MCU开发避坑:TLF35584看门狗喂不活?手把手教你SPI喂狗的正确姿势(附MPC5744代码)
  • 矢量网络分析仪原理
  • 打卡信奥刷题(3224)用C++实现信奥题 P8320 『JROI-4』Sunset
  • FastAPI CORS 跨域
  • 3DS FBI Link终极指南:Mac上最便捷的3DS文件传输工具
  • 从Windows 11到Nano Server:一张图看懂.NET 6与.NET 7的跨平台支持矩阵
  • 别再乱用 String 了!底层原理、常量池、拼接陷阱全解析
  • 2026年5月国内正规市场地位证明机构实测排行与能力解析 - 速递信息
  • 2026年最新市场地位认证技术维度解析与专业机构能力评估 - 速递信息
  • 使用OpenClaw构建AI智能体时配置Taotoken作为提供商
  • MPC-BE开源媒体播放器技术架构深度解析
  • ros2 从零开始19 使用 Node Interfaces 模板类(C++)
  • 2026 年孟德尔·格林伯格分享 OurCar 开发经验:解决家庭共享汽车难题!
  • QQ自定义在线状态改在线源码
  • FastAPI 静态文件
  • 【2026实战】双栈协同:Python+Go混合架构完整实战