Source
import __hello__ | Штука котра вартувала мені не однієї години дебагу.У starlette є два т...
633 Views/Reach
2025-05-24 19:31
Message №78
Штука котра вартувала мені не однієї години дебагу. У 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 який використовує contextvarsapp.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
Тобто якщо ви проставите значення у контекстварі усередині ендпоінта, то усередні мідлварі ви це значення вже не побачите. Здається звʼязане це із тим що у цьому випадку 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 який використовує contextvarsapp.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