Skip to content

中间件与责任链

责任链模式、ASGI 洋葱模型、@app.middleware、与 Java Filter 对比。

Updated View as Markdown
For humans

中间件与责任链

责任链的本意:多个处理器依次尝试,直到有人处理或链条走完。Web 框架的中间件是责任链的变体:每个环节都处理,请求穿过整条链。FastAPI/Starlette 的中间件基于 ASGI 洋葱模型。

责任链模式

class Handler:
    def __init__(self, next_handler=None):
        self._next = next_handler

    def handle(self, request):
        result = self._process(request)     # 本环节处理
        if result is None and self._next:   # 处理不了就传递
            return self._next.handle(request)
        return result

两种形态:

  • 传递型:处理不了才传给下一个(日志级别、审批流)
  • 全链型:每环都处理并传给下一个(Web 中间件)

ASGI 洋葱模型

洋葱模型: 请求逐层进入, 响应逐层返回

  • 请求从外到内穿过所有中间件到达应用,响应从内到外穿回
  • 每层可决定:放行(调 call_next)、拦截(直接返回,不进内层)、增强(响应回来再加工)

FastAPI 中间件

from starlette.middleware.base import BaseHTTPMiddleware

@app.middleware("http")async def timing_middleware(request, call_next):
    start = time.perf_counter()
    response = await call_next(request)      # 进入下一层
    response.headers["X-Time"] = str(time.perf_counter() - start)
    return response

要点:

  • call_next 是“进入内层”的开关,不调它请求就被拦截
  • 注册顺序决定执行顺序(先注册的在外层)
  • 纯 ASGI 中间件(类实现 __call__)性能更好,BaseHTTPMiddleware 有额外开销;生产级中间件(如 GZip)用纯 ASGI 形态

常用中间件场景:请求日志、鉴权、CORS、限流、响应压缩、统一错误处理。

与 Java 的对比

维度 FastAPI/Starlette Spring
模型 ASGI 洋葱(中间件包应用) Filter 链 + Interceptor
写法 装饰器或 ASGI 类 Filter 类 + 配置
顺序控制 注册顺序 @Order 注解
终止链路 不调 call_next 不调 chain.doFilter

FastAPI 的“依赖”也承担部分中间件职责(鉴权、会话),选择标准:横切所有请求用中间件,单接口逻辑用依赖

面试追问

  1. 洋葱模型是什么? 请求逐层进入、响应逐层返回。每层进出都有机会处理
  2. call_next 不调会怎样? 请求被拦截在本层,不进应用。返回自定义响应(鉴权失败、限流)
  3. 中间件和依赖怎么分工? 全局横切(日志、CORS)用中间件;接口级逻辑(鉴权、会话)用依赖。中间件先于依赖执行
  4. 注册顺序影响什么? 执行顺序。先注册的在外层,先处理请求、后处理响应
  5. BaseHTTPMiddleware 和纯 ASGI 的区别? 前者有额外开销,后者性能好。生产中间件用纯 ASGI 形态
Navigation

Type to search…

↑↓ navigate↵ selectEsc close