FastAPI 高级中间件完全指南:ASGI 中间件接入与 HTTPSRedirect、TrustedHost、GZip 的实战用法
FastAPI 基于 Starlette 构建并完整实现了 ASGI 规范,因此你可以在应用中接入任何符合 ASGI 规范的中间件。本篇指南以 官方文档 docs/fr/docs/advanced/middleware.md 为核心骨架,系统讲解如何用 app.add_middleware() 优雅地接入第三方 ASGI 中间件,并深入剖析 FastAPI 内置的 HTTPSRedirectMiddleware、TrustedHostMiddleware、GZipMiddleware 三个高频中间件的参数含义、默认值与底层实现。读完本篇,你将能独立完成 HTTPS 强制跳转、Host 头攻击防护与响应 GZip 压缩的配置,并能正确评估任意 ASGI 中间件在当前项目中的接入方式。
1. 知识前提:从自定义中间件与 CORS 谈起
在深入高级中间件之前,请确保你已经掌握两类基础用法:
- 自定义中间件:如何为一个 FastAPI 应用编写并挂载自己的中间件,参见 教程:自定义中间件;
- CORS 跨域:如何使用
CORSMiddleware处理浏览器跨域资源共享,参见 教程:CORS(CORSMiddleware)。
本节内容是它们在"通用 ASGI 中间件生态"维度的延伸——FastAPI 自带的基础教程只覆盖了少量场景,而真实生产环境还需要考虑安全跳转、Host 校验、传输压缩等能力,这些正是本篇要解决的核心问题。
2. 接入任意 ASGI 中间件:app.add_middleware()
2.1 为什么任何 ASGI 中间件都可以用
因为 FastAPI 基于 Starlette 且实现了 ASGI 规范,所以只要一个中间件遵循 ASGI 规范,它就不必专门为 FastAPI 或 Starlette 定制,也能直接工作。
通常,ASGI 中间件被设计为一个"类",其构造函数期待第一个参数是一个 ASGI 应用对象。因此第三方 ASGI 中间件的文档通常会引导你写成下面的包装(wrap)风格:
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
这种写法确实可行,但 FastAPI(实际上是其底层的 Starlette)提供了更简洁、更安全的接入方式——app.add_middleware()(就像你在 CORS 示例中看到的那样):
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
app.add_middleware() 接收一个中间件类作为第一个参数,之后的所有关键字参数都会被原样传给该中间件的构造函数。
2.2 为什么推荐 add_middleware() 而不是手动包装
从源码看,原因非常明确。在 fastapi/applications.py 中,FastAPI 把构造时传入的 middleware 保存在 self.user_middleware 列表里,并在 build_middleware_stack() 中把它嵌入一条精心编排的中间件链路:
ServerErrorMiddleware(服务器错误兜底)
└─ 用户通过 add_middleware 挂载的自定义中间件
└─ ExceptionMiddleware(异常处理分发)
└─ AsyncExitStackMiddleware(FastAPI 专用,负责关闭文件等资源)
└─ 真正的路由处理(Router)
也就是说,FastAPI 会自动保证:
- 内部
ServerErrorMiddleware能捕获并处理服务器错误(500),即使错误发生在你的自定义中间件内部; - 你注册的自定义异常处理器(如覆盖
HTTPException、校验错误)能够在正确的位置被触发; - FastAPI 特有的
AsyncExitStackMiddleware位于自定义中间件之后,用于正确关闭响应文件等资源、维持contextvars上下文。
如果采用手工 SomeMiddleware(app) 的包装方式,你就需要自己维护这套链路顺序,很容易把错误处理与异常分发放在错误的位置,导致中间件中的异常无法被正确转为响应。这正是文档强调"内部中间件处理服务器错误、自定义异常处理器正常工作"的底层保障。
3. 内置中间件:fastapi.middleware 模块导览
FastAPI 为常见的应用场景内置了一批中间件。一个值得注意的技术细节是:下面的示例你同样可以写成 from starlette.middleware.something import SomethingMiddleware。
FastAPI 在 fastapi.middleware 下提供这些中间件,仅仅是为了方便开发者统一从 fastapi 导入,而其中绝大多数实现直接来自 Starlette。打开仓库 fastapi/middleware 目录即可看到实际文件:
| 文件 | 内容 |
|---|---|
| httpsredirect.py | 从 Starlette 再导出的 HTTPSRedirectMiddleware |
| trustedhost.py | 从 Starlette 再导出的 TrustedHostMiddleware |
| gzip.py | 从 Starlette 再导出的 GZipMiddleware |
| cors.py | 从 Starlette 再导出的 CORSMiddleware |
| wsgi.py | 用于挂载 WSGI 应用的中间件 |
| asyncexitstack.py | FastAPI 内部使用的 AsyncExitStackMiddleware |
以 fastapi/middleware/httpsredirect.py 为例,它整个文件本质上只是:
from starlette.middleware.httpsredirect import ( # noqa
HTTPSRedirectMiddleware as HTTPSRedirectMiddleware,
)
所以"FastAPI 内置中间件"与"Starlette 中间件"在实现层面是同一份代码,导入路径不同而已。
下面逐一介绍三个最常用的安全与性能类中间件。
4. HTTPSRedirectMiddleware:强制 HTTPS / WSS 访问
4.1 作用
HTTPSRedirectMiddleware 强制所有进入应用的请求必须是 https 或 wss 协议;任何以 http 或 ws 发起的请求,都会被重定向到对应的安全协议(HTTP 响应码为 307 临时重定向)。
4.2 完整可运行示例
下面的完整示例来自 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"}
4.3 使用场景与注意事项
- 该中间件没有额外配置参数,注册即生效;
- 典型应用场景是生产环境已终止 TLS(如在 Nginx、反向代理、负载均衡层解密 HTTPS)后,在应用层兜底,确保即使客户端绕过代理直连应用,也不会通过明文 HTTP 传输数据;
- 需要留意:一旦开启,本地开发若直接以
http://127.0.0.1:8000访问,请求会被持续重定向到https://而无法到达路由——因此通常只在生产配置中启用,或配合环境判断按需注册。
5. TrustedHostMiddleware:校验 Host 头,抵御 Host 头攻击
5.1 作用
TrustedHostMiddleware 强制所有进入请求的 Host 头必须被正确设置且在允许名单内,用于抵御 HTTP Host Header 攻击(例如密码重置投毒、缓存污染等依赖伪造 Host 头的攻击)。一旦校验失败,中间件会直接返回 400 Bad Request 响应。
5.2 完整可运行示例
完整示例来自 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"}
5.3 支持的参数
| 参数 | 类型与说明 | 默认值 |
|---|---|---|
allowed_hosts |
允许作为主机名的域名列表。支持通配符域名(如 *.example.com 用于匹配所有子域名)。若要放行任意主机名,可设为 allowed_hosts=["*"],或者干脆不挂载该中间件 |
必填 |
www_redirect |
设为 True 时,对允许主机名"非 www 版本"的请求会重定向到其对应的"www 版本" |
True |
例如,配置了 allowed_hosts=["example.com", "*.example.com"] 后:
example.com与任意子域(a.example.com、b.example.com…)均被放行;- 由于默认
www_redirect=True,对www.example.com的访问会被保留,而对裸域与子域之间的差异按重定向策略处理; - 其他任意
Host(如evil.com)都会收到400响应。
5.4 实战建议
在部署于云负载均衡 / Kubernetes Ingress 等环境时,务必把域名白名单写全(主域名加 *. 通配),否则健康检查或服务间调用可能因 Host 校验不过而收到 400,造成误判宕机。反过来,若服务会被许多随机域名访问(如某些内部探活场景),则需显式使用 ["*"] 或移除该中间件。
6. GZipMiddleware:为响应开启 GZip 压缩
6.1 作用
GZipMiddleware 会为所有在 Accept-Encoding 请求头中包含 "gzip" 的请求,对响应体进行 GZip 压缩。它同时支持普通响应与流式响应(streaming responses)。
6.2 完整可运行示例
完整示例来自 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"
6.3 支持的参数
| 参数 | 类型与说明 | 默认值 |
|---|---|---|
minimum_size |
小于该字节数的响应不做 GZip 压缩(避免小响应因压缩头反而变大) | 500 |
compresslevel |
GZip 压缩级别,取 1~9 的整数。值越小压缩越快但文件更大;值越大压缩越慢但文件更小 | 9 |
6.4 设计细节与权衡
minimum_size的用意:对几十字节的小 JSON 响应做 GZip 没有收益(甚至因增加压缩元数据而变大),默认500字节的阈值能避免这种浪费;compresslevel的权衡:压缩级别本质是"CPU 时间 vs 带宽"的交换。高流量场景若 CPU 吃紧,可适当调低(如 5~6)以降低延迟;追求极致带宽节省时维持高位;- 源码中 FastAPI 对 gzip.py 只是从 Starlette 原样再导出,因此压缩算法实现细节与 Starlette 保持一致。
需要提醒:如果上游(如 Nginx、CDN)已经开启了 gzip,应用层再叠加压缩会造成双重压缩,通常建议二者只保留其一。
7. 其他 ASGI 中间件:生态远不止这些
ASGI 生态中还存在大量其他中间件,例如文档中列举的典型代表:
- Uvicorn 的
ProxyHeadersMiddleware(位于uvicorn.middleware.proxy_headers模块):用于解析反向代理设置的X-Forwarded-*头,正确还原客户端 IP 与协议。当你把 FastAPI 部署在 Nginx / Traefik 之后时,它与本文第 4、5 节的 HTTPS/Host 处理配合使用尤为关键; - MessagePack(msgpack-asgi):提供基于 MessagePack 二进制序列化协议的请求/响应支持。
文档还建议通过以下两个渠道发现更多可用中间件:Starlette 官方中间件文档(覆盖 Session、Authentication、CORSMiddleware、BaseHTTPMiddleware 等)以及社区整理的 ASGI Awesome 列表。接入这些第三方中间件时,统一使用 app.add_middleware() 即可获得本文第 2.2 节所述的错误处理与异常分发保障。
8. 小结:三种内置中间件的选型速查
| 中间件 | 核心职责 | 关键参数 | 典型场景 |
|---|---|---|---|
HTTPSRedirectMiddleware |
强制 https/wss,http/ws 自动 307 跳转 |
无 | 生产环境强制加密访问 |
TrustedHostMiddleware |
校验 Host 头合法性,非法返回 400 |
allowed_hosts(支持 *. 通配)、www_redirect(默认 True) |
抵御 HTTP Host Header 攻击 |
GZipMiddleware |
按 Accept-Encoding: gzip 对响应(含流式)做压缩 |
minimum_size(默认 500)、compresslevel(默认 9,范围 1~9) |
降低大响应传输体积 |
三者均从 fastapi.middleware 导入(实现来自 Starlette),注册方式统一为 app.add_middleware(SomeMiddleware, **options)。借助 fastapi/applications.py 中的 build_middleware_stack(),这些用户中间件会被正确嵌入 ServerErrorMiddleware 与 ExceptionMiddleware 之间,从而保证错误兜底与自定义异常处理器始终生效——这也是在生产中放心叠加中间件的关键前提。
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