У starlette є два типи мідлварів - BaseHTTPMiddleware та Pure ASGI Middleware. Я хз навіщо аж два, тим паче що "чисту" мідлварь писати не надто складніше ніж іншу.
Але у BaseHTTPMiddleware є особливість, на яку любʼязно вказує документація:
Using BaseHTTPMiddleware will prevent changes to contextvars.ContextVars from propagating upwards.
Тобто якщо ви проставите значення у контекстварі усередині ендпоінта, то усередні мідлварі ви це значення вже не побачите.
Здається звʼязане це із тим що у цьому випадку starlette копіює контекст та пропагує його у ендпоінти, але так як це копія - зміни зроблені у ньому не будуть відображатись у оригіналі.
Але ок, документація вказує що Pure ASGI Middleware не має цієї особливості.
To overcome these limitations, use pure ASGI middleware, as shown below.
Тож ок, мені треба реквесто-специфічний контекст, я для цього роблю свою "чисту" мідлварь, імплементую у ній що треба і все ок, так?
А от і ні, як виявляється, BaseHTTPMiddleware (тварюка така) не лише факапить контекст для себе, а вона ще й факапить контекст навіть для "чистих" мідлварей, якщо BaseHTTPMiddleware вказана першою.
Тобто отак наша мідлварь у якій ми використовуємо contextvars буде робити
app = Starlette()
app.add_middleware(PureMiddleware) # Pure ASGI Middleware який використовує contextvars
app.add_middleware(BaseMiddleware)
А отак вже не буде
app = Starlette()
app.add_middleware(BaseMiddleware)
app.add_middleware(PureMiddleware)
І в принципі до цього можна б було і своєю головою додуматись, бо по класиці мідлварі обгортають одна одну і та що зверху може афектити нижні.
Але дока це явно не пише, і до того ж ти явно можеш і не побачити що у тебе використовується мідлварь якщо юзаєш 3rd-party інтеграції (що зі мною і сталось)
Ось наприклад https://github.com/bobbui/json-logging-python використовує чогось саме BaseHTTPMiddleware.
Коротше якщо вам у starlette (і у fastapi, і подібних фреймворках) треба мідлварь котра використовує contextvars - ставте її завжди першою, бо однієї лише наявності
Pure ASGI Middleware, як виявилось, недостатньо
У першому коменті напишу приклад, як воно працює насправді.
Лінки:
- https://www.starlette.io/middleware/#basehttpmiddleware
- https://www.starlette.io/middleware/#pure-asgi-middleware