Какая middleware выполняется первой в FastAPI при нескольких объявленных
Что проверяют
Понимаете ли вы, как FastAPI собирает middleware в цепочку, а не просто помните порядок строк в файле. Интуиция «первым объявлен, первым выполнится» здесь неверна, и интервьюер ждёт именно этой поправки. Следом обычно спрашивают, куда ставить CORS и что увидит middleware, если обработчик бросил исключение.
Как отвечать
FastAPI наследует механизм от Starlette. Каждый вызов app.add_middleware() и каждый декоратор @app.middleware("http") вставляет новый слой в начало списка user_middleware. Получается луковица:
- запрос: первым его получает middleware, объявленный последним, затем предыдущий и так до роутера;
- ответ: идёт обратно, поэтому код после
await call_next(request)первым выполнится у самого раннего по объявлению слоя, а последним у самого позднего; - список в конструкторе: при
FastAPI(middleware=[Middleware(A), Middleware(B)])порядок прямой,Aвнешний и видит запрос первым; - служебные слои: снаружи всех пользовательских стоит
ServerErrorMiddleware, внутриExceptionMiddleware, который превращаетHTTPExceptionв обычный ответ.
Если объявить подряд три декоратора auth, logging и timing, на входе запрос пройдёт их в порядке timing, logging, auth, а на выходе в обратном. Проверить легко: поставьте print до и после call_next.
Отсюда правило: слой, который должен видеть всё (замер времени, трассировку, CORS), добавляют последним, чтобы он оказался внешним. Добавить middleware после старта приложения Starlette не даст, стек собирается один раз.
Где спотыкаются
Говорят, что порядок совпадает с порядком объявления. Для декораторов и add_middleware всё ровно наоборот, и на этом вопрос обычно заканчивается.
Добавляют CORSMiddleware первым, то есть в глубину цепочки. Тогда ответ 401 от внешнего слоя авторизации уходит без CORS-заголовков, и браузер показывает ошибку CORS вместо настоящей причины.
Ждут, что middleware поймает HTTPException из обработчика через try/except. Его перехватывает ExceptionMiddleware глубже по стеку, и снаружи приходит уже готовый ответ с кодом 4xx.