news 2026/5/7 23:03:45

FastAPI 中间件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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会完整执行中间件链。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 23:03:43

FastAPI CORS 跨域

FastAPI CORS 跨域学习笔记 一、什么是跨域问题 1. 同源策略 浏览器遵循同源策略(Same-Origin Policy),限制一个源的网页向另一个源发送请求。 同源 协议 域名 端口 三者一致:URL AURL B是否同源原因http://example.com/ahttp:…

作者头像 李华
网站建设 2026/5/7 23:02:35

ros2 从零开始19 使用 Node Interfaces 模板类(C++)

ros2 从零开始19 使用 Node Interfaces 模板类(C++) 前言 背景 并非所有ROS节点都一样! 就像类rclcpp::Node和类rclcpp_lifecycle::LifecycleNode不是来源同一个继承者,这意味着 ROS 开发者在编写一个以 ROS 节点指针为参数的函数时,可能会遇到类型的问题。 为解决这个…

作者头像 李华
网站建设 2026/5/7 23:02:33

Java后端面试:核心基础考点,String、StringBuilder、StringBuffer 区别详解

前言String、StringBuilder、StringBuffer 是Java面试最基础、出场率最高的知识点。大部分面试官都会询问三者区别、底层原理、线程安全以及使用场景。我整理了一套完整的面试知识点,通俗易懂,适合面试复习,也适合新手巩固Java基础。一、三者…

作者头像 李华
网站建设 2026/5/7 22:55:42

runprompt:基于Dotprompt格式的命令行LLM提示词工程化与自动化工具

1. 项目概述:runprompt,一个让LLM提示词在命令行中“活”起来的工具 如果你和我一样,日常工作中需要频繁地与各种大语言模型(LLM)打交道,无论是用它们来生成代码、分析日志,还是处理文档&#…

作者头像 李华
网站建设 2026/5/7 22:48:07

CSS如何改变鼠标指针样式_使用cursor属性优化交互体验.txt

根本原因是数据库未启用外部口令存储支持,需在sqlnet.ora中配置SQLNET.AUTHENTICATION_SERVICES(EXTERNAL)和WALLET_LOCATION,并确保路径正确、权限合法、客户端环境重载。Oracle Wallet 创建后 sqlplus / db 仍提示密码错误?根本原因不是 wa…

作者头像 李华