uvicorn不处理中间件注册,必须通过fastapi/starlette的app.add_middleware()或@app.middleware("http")注册;自定义中间件需严格遵循asgi 3协议签名,且注意uvicorn自动注入的proxyheadersmiddleware等外层中间件影响。

中间件必须通过 FastAPI/Starlette 注册,Uvicorn 本身不提供 add_middleware 方法
Uvicorn 是纯 ASGI 服务器,它只负责接收请求、调用 ASGI 应用、转发响应,**不处理中间件注册逻辑**。所谓“在 Uvicorn 中使用中间件”,实际是指把中间件注册到 FastAPI(或 Starlette)应用实例上,再由 Uvicorn 运行该应用。
常见错误是试图在 uvicorn.run() 调用里传入中间件参数,或直接调用 uvicorn.add_middleware() —— 这个函数根本不存在。
- ✅ 正确路径:定义
app = FastAPI()→ 调用app.add_middleware(...)或@app.middleware("http") - ❌ 错误操作:
uvicorn.add_middleware(...)、uvicorn.run(..., middleware=[...])(无此参数) - ⚠️ 注意:
uvicorn.Config类虽有middleware属性,但它仅用于内部构造 ASGI app 包装链(如ProxyHeadersMiddleware),**不开放给用户直接配置业务中间件**
app.add_middleware() 与 @app.middleware("http") 的行为差异
两者最终都注册到 Starlette 的中间件栈,但执行时机和封装粒度不同:
-
@app.middleware("http"):装饰器形式,接收request: Request和call_next,适合轻量拦截(如日志、头信息修改);每个装饰器生成一个独立中间件类实例 -
app.add_middleware(MiddlewareClass, **kwargs):显式传入中间件类(需符合 ASGI 协议),适合复用已有中间件(如CORSMiddleware、自定义类),支持传参初始化 - 执行顺序:所有
@app.middleware按代码顺序注册,排在app.add_middleware()之前;即先运行装饰器中间件,再运行add_middleware注册的
示例中若同时用了两个 @app.middleware 和一个 app.add_middleware(CORSMiddleware),CORS 头会最后写入响应,可能覆盖前面中间件设置的同名 header。
Uvicorn 0.52.1 是 Python ASGI Web 服务器,提供异步应用服务及 WebSocket 支持,常用于 FastAPI、Starlette 等应用的开发和部署。本下载为官方 PyPI 通用 wheel 包,可使用 pip 安装,并由包管理器处理所需依赖。
自定义中间件类必须实现 ASGI 3 协议签名
手动写中间件类时,最容易出错的是函数签名不匹配。Uvicorn 运行时严格校验 __call__ 方法是否符合 ASGIApp 类型:
- ❌ 错误写法:
async def __call__(self, scope, receive, send)缺少类型注解或参数名不一致 - ✅ 正确签名:
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None - ⚠️
Scope、ASGIReceiveCallable等类型需从starlette.types或typing导入,否则运行时可能不报错但逻辑异常 - 性能提示:中间件内避免阻塞调用(如同步 DB 查询、
time.sleep()),否则拖垮整个异步 pipeline
一个最小可用中间件类:
from starlette.types import ASGIApp, Scope, Receive, Send
class SimpleLogMiddleware:
def __init__(self, app: ASGIApp):
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
print(f"Request: {scope['method']} {scope['path']}")
await self.app(scope, receive, send)
Uvicorn 启动时自动注入的中间件会影响你的调试
当你启用 --proxy-headers 或开启 debug 日志(--log-level trace),Uvicorn 会在底层自动包装你的 ASGI app:
-
ProxyHeadersMiddleware:重写scope["client"]和scope["scheme"],若你中间件依赖原始 client IP,需注意它已被替换 -
MessageLoggerMiddleware:在 trace 级别下打印完整 ASGI message 流,可能干扰你自己的日志格式或造成大量输出 - 这些中间件位于你注册的中间件**之外层**(即最先被调用),所以它们看到的是最原始的请求,而你的中间件看到的是已被代理头处理过的
scope
调试时若发现 client IP 总是 127.0.0.1,先检查是否漏配 --proxy-headers 或 --forwarded-allow-ips,而不是怀疑自己中间件写错了。










