最稳妥方式是继承BaseHTTPMiddleware并重写dispatch方法:在call_next前用time.perf_counter()记录起始时间,call_next后计算差值,将结果以X-Process-Time头写入Response并返回;必须确保返回Response实例且避免同名header覆盖。

用 BaseHTTPMiddleware 写耗时统计中间件最稳妥
直接继承 BaseHTTPMiddleware 是当前 FastAPI(v0.112+)推荐方式,比旧的 @app.middleware("http") 装饰器更清晰、类型更安全,也方便复用和单元测试。
关键点在于:必须调用 call_next(request) 才能让请求继续往下走;耗时计算要包住它;响应头写入必须在 call_next 之后。
-
start_time放在call_next前,别漏了 - 别用
time.time()测微秒级差异,改用time.perf_counter() - 返回的
response必须是Response实例,不能是 dict 或其他类型,否则会报TypeError: object of type 'dict' has no len()
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
import time
<p>class TimingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next) -> Response:
start_time = time.perf_counter()
response = await call_next(request)
process_time = time.perf_counter() - start_time
response.headers["X-Process-Time"] = f"{process_time:.6f}"
return response
</p>
注册中间件时顺序影响耗时结果
中间件是洋葱模型,越早注册的越外层。如果你同时用了日志、鉴权、压缩等中间件,TimingMiddleware 的位置决定了它测的是“纯业务耗时”还是“全链路耗时”。
- 想测真实业务逻辑耗时 → 把
TimingMiddleware放在鉴权、日志之后,靠近路由层 - 想测端到端延迟(含鉴权、反向代理开销等)→ 放最外层,第一个注册
- Uvicorn 默认有
TimeoutMiddleware,如果它在TimingMiddleware外层,超时错误的耗时也会被统计进去
注册示例:
app.add_middleware(TimingMiddleware) # 最外层 app.add_middleware(AuthMiddleware) # 第二层 app.add_middleware(LoggingMiddleware) # 第三层
dispatch 方法里不能直接 return dict 或 str
这是新手最容易踩的坑:看到示例里返回 response 就以为可以随便 return,结果一加中间件整个服务就崩了。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 所有中间件的
dispatch方法签名都要求返回Response类型 - 如果你在中间件里想提前终止请求(比如鉴权失败),必须手动构造
JSONResponse或PlainTextResponse,不能写return {"detail": "unauthorized"} - 耗时中间件一般不拦截,所以不用构造新响应,但一旦和其他中间件组合,就得注意返回值类型是否一致
错误写法:
# ❌ 这会导致 TypeError
if not is_valid_token(request):
return {"error": "Unauthorized"} # 不是 Response 实例
生产环境要注意 header 值精度和日志落盘开销
X-Process-Time 头默认只对调试友好,真上生产得考虑两点:
- 精度太高(如
.9f)可能暴露内部调度细节,建议用.6f足够,单位是秒 - 如果同时开启详细日志(比如打印 query_params 或 body),记得加
LOG_LEVEL != "DEBUG"判断,避免大请求体拖慢整个链路 - 高 QPS 场景下,频繁调用
time.perf_counter()几乎无开销,但拼接字符串 + 写 header 有微量成本,实测单次增加约 0.02ms,可忽略
真正容易被忽略的是:当多个中间件都往 response.headers 写同名 key 时,后写的会覆盖前写的 —— 所以如果你自定义了多个耗时中间件,别都用 X-Process-Time,改用不同 key,比如 X-Auth-Time、X-DB-Time。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










