写后端接口写多了,你大概率会遇到这样的需求。想给每个请求都打印一条日志,想统一给响应加个处理耗时的标记,想拦截来路不明的跨域请求,或者干脆想在请求真正进入业务逻辑之前先做一轮安全检查。这些零零散散的诉求,其实都指向同一个技术概念------中间件。
FastAPI官方文档把中间件定义得很直白,它是一个在请求被具体路径操作处理之前、以及响应被返回之前都会执行的函数 。这句话听起来有点绕,但换个角度理解就简单多了。你可以把它想象成机场的安检口,每一位旅客(也就是每一个HTTP请求)在登机(进入具体的业务逻辑)之前,都得先过一遍安检。安检员会检查你的证件、你的行李,觉得没问题才放行。返程的时候,安检口可能也会对你做个简单的核对。中间件干的就是这个活儿------拦截、检查、放行,两头都管。
中间件的工作原理,其实就是一条流水线
FastAPI里创建中间件最直接的方式是用装饰器 @app.middleware("http"),装饰一个异步函数。这个函数接收两个参数,一个是 request,另一个是 call_next------一个可调用对象,负责把请求继续往下传,传给真正对应的路径操作函数处理,然后把处理完的响应结果返回来 。
官方给的经典示例是记录请求耗时,代码大致长这样:
python
import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.perf_counter()
response = await call_next(request)
process_time = time.perf_counter() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
这段代码的逻辑很清晰。请求进来先记一个开始时间,然后交给 call_next 去跑真正的业务逻辑,等业务逻辑处理完返回响应,再算一下耗时,塞进响应头里。整个过程你完全可以理解成------请求经过之前做点什么,响应返回之前再做点什么,两次介入的机会都留给了开发者。
如果你注册了多个中间件,执行顺序会变得有点像套娃。FastAPI官方文档专门讲了这一点,每新增一个中间件,都会把之前的应用包一层,最后添加的中间件在最外层,最先添加的反而在最里层 。举个例子,假设你依次执行了:
scss
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
那么请求进来的顺序是 MiddlewareB → MiddlewareA → 路由处理函数 ,而响应返回的顺序恰好反过来,路由处理函数 → MiddlewareA → MiddlewareB。这种先进后出的栈式结构,说白了就是俄罗斯套娃,最后套上去的那层反而是最先接触外界的那层。搞清楚这个顺序特别重要,因为很多安全类中间件(比如CORS检查、Host校验)就是靠这个执行顺序来生效的,顺序错了可能直接导致安全策略形同虚设 。
下面用一张图把这个流转过程画出来,看着会更直观一些:

中间件在安全防护上到底能做什么
聊中间件绕不开安全这个话题,因为它天生就长在请求处理链条的最前端,是拦截恶意流量最合适的位置之一。这里挑几个最典型、最常用的安全场景说说。
跨域请求的拦截------CORS中间件
如果你的前端和后端部署在不同的域名下,浏览器出于安全考虑会默认拦截跨域请求,这就是所谓的同源策略 。FastAPI提供了现成的 CORSMiddleware,用来告诉浏览器哪些域名允许访问你的接口 、允许哪些HTTP方法 、是否允许携带Cookie 。
ini
from fastapi.middleware.cors import CORSMiddleware
origins = [
"https://example.com",
"https://www.example.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["*"],
)
这里有个坑很多新手会踩。allow_origins 千万别图省事直接写成通配符 ["*"],尤其是在 allow_credentials=True 的情况下,这基本等于把大门敞开,谁都能带着Cookie访问你的接口。安全领域的建议一直很一致------只放行你明确信任的域名,只开放业务真正需要的HTTP方法,能收紧就绝不放宽 。
防范Host头攻击------TrustedHostMiddleware
这个中间件知道的人相对少一些,但作用很关键。它的职责是强制校验每个进来的请求,Host请求头必须匹配你预先设定好的白名单 ,否则直接拒绝 。为什么这个很重要?因为如果不做这层校验,攻击者可以伪造Host头,诱导你的应用生成带有恶意域名的链接(比如密码重置邮件里的链接),这种攻击手法叫Host Header注入,杀伤力不小。
css
from starlette.middleware.trustedhost import TrustedHostMiddleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["example.com", "*.example.com"],
)
限流与IP黑白名单
生产环境里裸奔的接口很容易被爬虫或者恶意脚本刷爆,这时候限流类中间件就派上用场了。社区里有个叫 FastAPI Guard 的扩展,专门做这块------IP白名单黑名单管理、自动限流、异常IP自动封禁,一整套安全能力打包给你 。当然你也可以自己写一个简易版本,核心逻辑无非是记录每个IP的访问频次,超过阈值就直接返回429状态码。
一些实践层面的经验之谈
写中间件这事儿说难不难,但踩坑的人真不少。有几条经验值得记一下 :
- 别让一个中间件干太多事,日志归日志,鉴权归鉴权,职责拆开写,排查问题的时候你会感谢自己的
- 中间件的注册顺序要想清楚,比如安全类的检查(Host校验、限流)最好放在靠外层,尽早拦截掉不合法的请求,省得浪费后面的计算资源
- 别在响应头里泄露敏感信息,服务器版本号、内部错误堆栈这些东西,能藏就藏
- 中间件里的逻辑尽量轻量,毕竟每一个请求都要经过这一层,代码写重了会拖累整体性能
中间件的常见使用场景
说了这么多安全相关的内容,其实中间件的用武之地远不止于此。下面这张表大致梳理了几类典型场景:
| 场景类型 | 典型用途 | 常用工具/写法 |
|---|---|---|
| 请求日志与监控 | 记录请求路径、耗时、状态码,方便排查问题 | 自定义 @app.middleware("http") |
| 跨域处理 | 允许指定前端域名访问后端接口 | CORSMiddleware |
| 安全防护 | 校验Host头、限流、IP黑白名单 | TrustedHostMiddleware、FastAPI Guard |
| 响应压缩 | 减小响应体积,提升传输效率 | GZipMiddleware |
| 强制HTTPS | 把HTTP请求重定向到HTTPS | HTTPSRedirectMiddleware |
| 统一异常处理 | 捕获未被处理的异常,返回统一格式的错误响应 | 自定义中间件 + 异常处理器 |
这几类场景基本覆盖了实际项目里九成以上的中间件用法 。尤其是日志监控 和安全防护这两块,几乎是所有生产级API都绕不开的标配,很多团队会把中间件当成搭建可观测性体系的第一道关卡。
什么时候真正需要动用中间件
不是所有的通用逻辑都得往中间件里塞,这里有个判断标准值得琢磨。如果这段逻辑需要对系统里几乎所有请求都生效,并且和具体的业务无关,那它多半适合放进中间件。反过来,如果某个校验只针对特定的几个接口,用FastAPI的依赖注入系统(Dependency)会更合适,粒度更细,也更符合单一职责的设计思路。
举几个具体的判断例子会更清楚:
- 想给所有接口统一加个请求耗时的响应头------用中间件,因为这跟具体业务无关,适用范围是全局的
- 想校验某个特定接口的用户Token有没有过期------用依赖注入,因为这是针对具体接口的业务校验
- 想拦截所有跨域请求做统一处理------用中间件 ,
CORSMiddleware天生就是干这个的 - 想在某几个管理员专属接口上加一层额外的权限校验------用依赖注入,不需要影响到其他接口
这个取舍其实挺考验架构判断力的。中间件的好处是一次配置、全局生效,省心省力,但代价是它没法感知到具体路由的业务上下文,逻辑写复杂了反而容易变成一团难以维护的黑盒。而依赖注入虽然要在每个需要的接口上显式声明,但胜在灵活可控,出问题也容易定位到具体是哪个接口的锅。
写在最后
中间件这套机制,本质上是给整个应用的请求处理流程开了一个统一的切口,安全检查、日志记录、性能监控这些横切关注点(cross-cutting concerns)都能顺理成章地挂在这个切口上,不用在每个路径操作函数里重复写一遍。用好了它,你的API会显得规整很多,安全性也能提升一个台阶。但也别滥用,把不该放进去的业务逻辑硬塞进中间件,最后维护起来只会变成一场噩梦。
搞清楚什么时候用中间件、什么时候用依赖注入,这条分界线画明白了,代码架构自然就顺了。
参考资料
FastAPI Official Documentation - Middleware. fastapi.tiangolo.com/tutorial/mi...
FastAPI 官方文档(中文)- 中间件. fastapi.tiangolo.com/zh/tutorial...
FastAPI Official Documentation - CORS (Cross-Origin Resource Sharing). fastapi.tiangolo.com/tutorial/co...
How to Add Middleware to FastAPI - OneUptime Blog. oneuptime.com/blog/post/2...
Secure Configuration of CORS in FastAPI - CodeSignal. codesignal.com/learn/cours...
FastAPI TrustedHostMiddleware refuses my host - Stack Overflow. stackoverflow.com/questions/7...
FastAPI Guard - A FastAPI extension to secure your APIs - Reddit r/Python. www.reddit.com/r/Python/co...
Middleware in FastAPI: A Practical and Comprehensive Guide - Python in Plain English. python.plainenglish.io/middleware-...
Building Production-Ready APIs with FastAPI - Medium. dev-faizan.medium.com/building-pr...