FastAPI 踩坑实战记录 -- Middleware的顺序问题

1,255 阅读1分钟

FastAPI 的 Middleware 添加顺序对响应结果有影响么

答案是:有

比如下面这段代码

app.add_middleware(
    AuthenticationMiddleware, backend=BasicAuthBackend()
)
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

比较简单,是先后添加了“简单的认证”和“CORS跨域”两个中间件,期待的功效是两个中间件都能发挥作用。

通常情况下,没问题,都可以正常工作,但是后一个中间件的返回结果会被前一个覆盖,如果有异常的话。

怎么理解呢,可以给这两个中间件的add 顺序反过来,变成下边这个样子:

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
app.add_middleware(
    AuthenticationMiddleware, backend=BasicAuthBackend()
)

一个简单的实验是,token过期,会触发AuthenticationMiddleware的Error,但是前端页面上并不知情(不知道具体什么错误),前端看起来是CORS跨域异常了。。。所以把这两个添加顺序反一下即可。 粗略看了一下 add_middlewre()方法的源代码:

self.middleware_stack = self.build_middleware_stack()
def build_middleware_stack(self) -> ASGIApp:
    debug = self.debug
    error_handler = None
    exception_handlers = {}

    for key, value in self.exception_handlers.items():
        if key in (500, Exception):
            error_handler = value
        else:
            exception_handlers[key] = value

    middleware = (
        [Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug,)]
        + self.user_middleware
        + [
            Middleware(
                ExceptionMiddleware, handlers=exception_handlers, debug=debug,
            )
        ]
    )

    app = self.router
    for cls, options in reversed(middleware):
        app = cls(app=app, **options)
    return app

可能跟最后reversed的时候有关,也正如其名,是一个stack,所以第一个add进来的middleware会最后被response给请求,从而错过了真实的error信息。 不过多说一句,FastAPI的这个

app.debug = True

没有它看起来这么简单

@property
   def debug(self) -> bool:
       return self._debug

@debug.setter
def debug(self, value: bool) -> None:
   self._debug = value
   self.middleware_stack = self.build_middleware_stack()

一个平平无奇的debug设置,要重新build一次middleware stack!

仅作为自己学习使用fastapi的一点心得笔记,如果对你有帮助,那就太好了!