FastAPI 高级中间件(Advanced Middleware)完全指南:集成任意 ASGI 中间件与内置中间件实战
本篇技术指南以 FastAPI 高级用户指南中的 Middleware Avanzado(高级中间件)章节(英文原版)为核心,系统讲解 FastAPI 项目中中间件的进阶用法:如何把任意符合 ASGI 规范的第三方中间件接入应用,以及仓库内置/内置集成的 HTTPSRedirectMiddleware、TrustedHostMiddleware、GZipMiddleware 等中间件的正确配置方式与参数语义。读完本文,你将掌握 app.add_middleware() 的接入机制、各内置中间件的安全与性能价值,并能在自己的 FastAPI 应用中独立完成 HTTPS 强制跳转、Host 校验和 GZip 压缩等生产级配置。
在正文开始之前,建议先回顾两篇前置章节:自定义中间件的创建方式见 Middleware 教程章节,跨域场景的 CORSMiddleware 用法见 CORS 章节。本文讨论的是这两者之外的“其他中间件”用法。
FastAPI 为什么可以接入任意 ASGI 中间件
FastAPI 是基于 Starlette 构建的,而 Starlette 完整实现了 ASGI 规范(异步服务器网关接口)。这意味着:
- 只要一个组件遵循 ASGI 规范,它就不需要专门为 FastAPI 或 Starlette 定制,也能无缝接入应用;
- 一般而言,ASGI 中间件就是一类“期望把某个 ASGI 应用作为第一个构造参数接收”的类,通过“包裹”下一层应用实现对请求/响应的拦截与加工。
在第三方 ASGI 中间件的文档中,你常见到的是类似下面的用法——直接手工构建新的应用对象:
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
用 app.add_middleware() 接入第三方 ASGI 中间件
直接手工包裹的问题是:这样生成的 new_app 脱离了 FastAPI 内部的中件夹栈管理,无法保证服务端错误处理(ServerErrorMiddleware)与自定义异常处理器(ExceptionMiddleware)正常工作。因此 FastAPI(更准确说是 Starlette)提供了更简单也更稳妥的方式——app.add_middleware():
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
app.add_middleware() 第一个参数接收中间件类,其后紧跟任意需要传递给该中间件的附加参数(构造参数),如这里的 some_config="rainbow"。
从源码看 add_middleware 的中间件栈机制
为什么用 add_middleware 就能保证异常处理正常?可以从仓库的 FastAPI 实现找到依据:
FastAPI类本身继承自 Starlette(见 fastapi/applications.py),因此add_middleware()方法来自 Starlette 基类;- FastAPI 在
__init__中维护self.user_middleware列表(fastapi/applications.py),每次调用add_middleware()就是把中间件类追加进该列表,随后触发setup()重建中间件栈; - FastAPI 重写了
setup()(fastapi/applications.py)与build_middleware_stack()(fastapi/applications.py),其核心组装逻辑是:
[ServerErrorMiddleware] + self.user_middleware + [ExceptionMiddleware, AsyncExitStackMiddleware]
也就是说,你通过 add_middleware() 注册的中间件会落在 ServerErrorMiddleware 内部、ExceptionMiddleware 外部。这样 500 级服务器错误仍会被最外层兜底处理,路由抛出的异常也仍会经过你注册的中间件链最终交给自定义异常处理器——这正是文档强调“内部中间件处理服务器错误、自定义异常处理器正常工作”的源码级原因。FastAPI 在构建栈时额外加入了 AsyncExitStackMiddleware(位于最内层),用于关闭依赖与上传文件等资源。
关于顺序:后添加的中间件更“靠外”
如果在构造阶段传入 middleware 参数,或在运行期多次调用 add_middleware(),每个新中间件都会包裹已存在的应用形成栈。请求进入时最外层先执行,响应返回时最后执行;FastAPI 会把栈重建为上述“ServerError → 用户中间件 → Exception → AsyncExitStack → 路由”的结构,具体顺序语义可参见 Middleware 教程中的“多中间件执行顺序”小节。除运行期 add_middleware() 外,还可用 Starlette 的 Middleware 类在构造 FastAPI(middleware=[...]) 时声明式传入(Middleware 已由 fastapi/middleware/init.py 从 Starlette 再导出)。
仓库自带的中间件模块一览(fastapi.middleware)
文档有一处专门的“细节说明”:下文示例中你其实也可以直接写 from starlette.middleware.something import SomethingMiddleware。FastAPI 在 fastapi.middleware 下提供这些中间件,纯粹是为了开发者便利——它们绝大多数直接来自 Starlette。这一点在源码中得到印证:fastapi/middleware/ 下的模块基本都是对 Starlette 同名类的极薄再导出:
| fastapi.middleware 模块 | 实际来源 | 用途 |
|---|---|---|
| httpsredirect.py | starlette.middleware.httpsredirect.HTTPSRedirectMiddleware |
强制 HTTPS/WSS |
| trustedhost.py | starlette.middleware.trustedhost.TrustedHostMiddleware |
Host 头白名单校验 |
| gzip.py | starlette.middleware.gzip.GZipMiddleware |
GZip 响应压缩 |
| cors.py | starlette.middleware.cors.CORSMiddleware |
跨域资源共享 |
| asyncexitstack.py | starlette.middleware.asyncexitstack.AsyncExitStackMiddleware |
栈内资源清理 |
| wsgi.py | starlette.middleware.wsgi.WSGIMiddleware |
挂载 WSGI 应用 |
| init.py | starlette.middleware.Middleware |
声明式中间件组合类 |
CORSMiddleware 的具体用法在 CORS 教程 已有专门讲解,本节不再重复。下面按文档顺序深入三个最常用的内置中间件。
HTTPSRedirectMiddleware:强制 HTTPS/WSS 跳转
该中间件强制所有入站请求必须为 https 或 wss:任何以 http 或 ws 到达的请求都会被 307 重定向到对应的安全协议。适合部署在 HTTPS 终结(如反向代理之后)场景下进一步兜底,防止明文协议被直接访问。
完整可运行示例见 docs_src/advanced_middleware/tutorial001_py310.py:
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
@app.get("/")
async def main():
return {"message": "Hello World"}
仓库测试如何验证它
仓库测试 tests/test_tutorial/test_advanced_middleware/test_tutorial001.py 从两个方向验证了该中间件:
- 当
TestClient使用base_url="https://testserver"发起请求时,直接返回200; - 当使用默认的
http协议请求且关闭自动跟随重定向(follow_redirects=False)时,返回状态码307,且响应头location为https://testserver/。
这组用例也提醒你:该中间件生效的前提是上游真的终结了 TLS——它只负责把明文流量“导流”到安全入口,并不自己做加解密。
TrustedHostMiddleware:防御 HTTP Host 头攻击
TrustedHostMiddleware 强制所有入站请求携带正确的 Host 头,从而防御 HTTP Host Header 攻击(如密码重置钓鱼、缓存投毒等依赖篡改 Host 头的攻击手段)。若入站请求校验不通过,中间件会直接返回 400 响应。
完整示例见 docs_src/advanced_middleware/tutorial002_py310.py:
from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
app.add_middleware(
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
)
@app.get("/")
async def main():
return {"message": "Hello World"}
支持的构造参数
allowed_hosts:一个允许作为主机名的域名列表。支持通配符域名,例如*.example.com会匹配其所有子域名。若想允许任意主机名,要么显式传allowed_hosts=["*"],要么干脆不挂载该中间件(因为"*"相当于关闭校验)。www_redirect:设为True时,对允许主机列表中“非 www”版本域名的请求会被 307 重定向到带www的对应地址;默认值为True。
文档明确说明:一旦入站请求未通过校验,会收到 400 响应。
仓库测试如何验证它
test_tutorial002.py 用三种 base_url 演示了判定规则:
| base_url | 结果 |
|---|---|
http://example.com |
200(命中白名单) |
http://subdomain.example.com |
200(被 *.example.com 通配匹配) |
http://invalidhost |
400(不在白名单) |
实际接入时需要把你所有的对外域名(含可能用到的子域名)都放进 allowed_hosts,并留意 www_redirect=True 的默认重定向行为是否符合你的域名规划。
GZipMiddleware:为响应启用 GZip 压缩
GZipMiddleware 会对在 Accept-Encoding 请求头中包含 "gzip" 的请求返回 GZip 压缩后的响应,从而减小传输体积、降低带宽消耗。值得注意的细节是:它既能处理标准响应,也能处理流式(streaming)响应。
完整示例见 docs_src/advanced_middleware/tutorial003_py310.py:
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=5)
@app.get("/")
async def main():
return "somebigcontent"
支持的构造参数
minimum_size:小于该字节数的响应不做 GZip 压缩。默认值为500(字节)。这是为了“小响应不值得压缩”的工程权衡——压缩小响应反而可能因头部开销得不偿失。compresslevel:GZip 压缩过程中使用的压缩级别,取值为1到9的整数,默认值为9。级别越低压缩越快但产物越大,级别越高压缩越慢但产物越小。在 CPU 敏感或流量峰值场景可调低以换取吞吐。
文档示例即为“不是默认参数”的实践:minimum_size=1000(小于 1KB 不压缩)配合 compresslevel=5(速度与体积的折中)。
仓库测试如何验证它
test_tutorial003.py 为同一个 app 额外注册了一条返回 4000 个 x 字符的路由 /large,然后断言:
- 请求携带
accept-encoding: gzip时响应状态为200; - 响应头
Content-Encoding等于gzip; - 压缩后的
Content-Length数值小于原始的4000; - 请求不带 gzip 编码的根路径
/时行为正常。
如果你想亲手验证,可把任一示例保存为 main.py 后用 uvicorn main:app 启动,再配合浏览器开发者工具或 curl --compressed 观察响应头中的 Content-Encoding: gzip。
其他 ASGI 中间件生态与延伸阅读
FastAPI 的中间件体系并不局限于上述三者。因为 ASGI 规范天然具备互操作性,生态里还活跃着大量同类中间件,例如:
- Uvicorn 的
ProxyHeadersMiddleware:在反向代理(如 Nginx)之后,用于根据代理头还原客户端真实 IP 与协议信息,对日志审计、限流、HTTPS 判定等场景很重要; - MessagePack 等序列化类 ASGI 中间件:为需要紧凑二进制载荷的接口提供备选编解码路径;
- Starlette 官方维护的其他中间件:如会话管理
SessionMiddleware、认证AuthenticationMiddleware、自定义异常响应ExceptionMiddleware等,均在 Starlette 中间件文档中有完整说明,通常只需from starlette.middleware.xxx import XxxMiddleware引入后按前文方式注册即可。
需要再次强调的是:接入任何第三方 ASGI 中间件时,优先使用 app.add_middleware() 而不是手工包裹应用对象,这样你得到的中间件栈仍处于 FastAPI/Starlette 的管理之下,服务器错误兜底与自定义异常处理器都不会被绕过。这与本仓库源码中 build_middleware_stack() 的实现(fastapi/applications.py)保持一致。
小结
本文围绕 Advanced Middleware 文档 完整覆盖了三个层面的知识:
- 接入机制:FastAPI 基于 Starlette/ASGI,可用
app.add_middleware()注册任意 ASGI 中间件;源码证实用户中间件位于ServerErrorMiddleware之内、ExceptionMiddleware之外,因此错误处理不被绕过; - 内置中间件:
HTTPSRedirectMiddleware(强制 HTTPS/WSS)、TrustedHostMiddleware(allowed_hosts+www_redirect,非法 Host 返回 400)、GZipMiddleware(minimum_size默认 500、compresslevel默认 9、范围 1–9),每个均有文档示例与仓库测试用例双保险; - 生态延展:
fastapi.middleware各模块实为 Starlette 同名类的再导出,此外 Uvicorn、Starlette 与 ASGI 社区还有更丰富的中间件可选用。
掌握了这些中间件与注册顺序规则,你就可以在 FastAPI 应用中可靠地叠加 HTTPS 强制、Host 校验、GZip 压缩等生产级能力,而无需担心它们破坏框架自身的异常处理链路。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00