Что такое middleware и как его использовать в Python?

Ответ

Middleware (промежуточное ПО) — это компонент, который обрабатывает HTTP-запросы и ответы до или после их обработки основным обработчиком (view-функцией) в веб-фреймворках. Он позволяет выполнять общую логику, не дублируя ее в каждом эндпоинте.

Пример использования в FastAPI:

from fastapi import FastAPI, Request, Response
import time

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    # Логика до обработки запроса (например, логирование, аутентификация)
    print(f"Request received: {request.url}")

    response = await call_next(request)

    # Логика после обработки запроса (например, добавление заголовков, сжатие)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(round(process_time, 4))
    print(f"Request processed in {process_time:.4f} seconds")
    return response

@app.get("/")
async def read_root():
    return {"message": "Hello World"}

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

Объяснение:

  1. Декоратор @app.middleware("http") регистрирует асинхронную функцию add_process_time_header как middleware для HTTP-запросов.
  2. request: Request — объект входящего запроса.
  3. call_next — функция, которая передает управление следующему middleware или основному обработчику маршрута. Ее вызов возвращает Response.
  4. Код до await call_next(request) выполняется до обработки запроса.
  5. Код после await call_next(request) выполняется после обработки запроса, позволяя модифицировать ответ.

Преимущества использования middleware:

  • Централизация логики: Аутентификация, логирование, обработка ошибок, добавление заголовков, сжатие ответов.
  • Повторное использование: Избегание дублирования кода в различных эндпоинтах.
  • Модульность: Легкое добавление или удаление функциональности без изменения основной бизнес-логики.

Ответ 18+ 🔞

Так, слушай, про middleware, это же просто пиздец какая удобная штука, если врубиться. Представь себе, блядь, бармена в баре, да? Не каждый же клиент сразу к стойке прёт, чтобы ему налили. Сначала швейцар дверь открывает, потом гардеробщик куртку принимает, потом охранник смотрит, не пиздит ли кто-нибудь чего. Вот эти все ребята — они и есть middleware, ёпта! Они запрос (клиента) обрабатывают, пока он до главного обработчика (бармена) не дойдёт.

А в коде это выглядит примерно так, смотри, не усни только:

from fastapi import FastAPI, Request, Response
import time

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    # Вот тут мы делаем что-то ДО того, как запрос попадёт куда надо
    print(f"Пришёл запрос на: {request.url}")

    response = await call_next(request)  # А это — священный момент! Передаём эстафету дальше!

    # А вот тут мы уже ПОСЛЕ того, как бармен (обработчик) отработал
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(round(process_time, 4))
    print(f"Запрос обработан за {process_time:.4f} секунд, ядрёна вошь!")
    return response

@app.get("/")
async def read_root():
    return {"message": "Hello World"}

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

Чё тут вообще происходит, по косточкам:

  1. @app.middleware("http") — это как повесить табличку «Здесь сидит тётенька, которая всех пропускает через себя». Функция под ним становится этим самым посредником.
  2. request: Request — это сам клиент, который пришёл, со всеми своими хотелками (заголовками, куками и прочей хуйнёй).
  3. call_next — это, блядь, волшебная кнопка «ПУСК». Ты её жмёшь и говоришь: «Ну всё, иди дальше по цепочке, пусть следующий мудак разбирается».
  4. Всё, что до await call_next(request) — выполняется ДО основной логики. Типа «ой, кто это к нам пришёл, давайте засечём время, проверим права».
  5. Всё, что после — это уже ПОСЛЕ. Ответ от бармена уже есть, можно к нему прилепить какую-нибудь бумажку (заголовок), сжать его или в лог записать, сколько времени он там бултыхались.

А зачем это всё, спрашивается? Ну вот, блядь, преимущества:

  • Не повторяйся, мудила! Вместо того чтобы в КАЖДОЙ view-функции писать одну и ту же проверку на авторизацию или логирование, ты пишешь это один раз в middleware — и оно работает на все роуты автоматом. Красота, ёпта!
  • Всё по полочкам. Аутентификация, логи, обработка ошибок, CORS, сжатие ответов — всё это можно вынести в отдельные middleware-компоненты. Получается как конструктор: нужна аутентификация — добавил компонент, не нужна — выкинул. Никакой головной боли с основной бизнес-логикой.
  • Центр управления полётами. Вся общая, скучная, но необходимая хуйня живёт в одном месте, а не размазана по всему проекту. Подозрение ебать чувствую, что без этого жить можно, но с ним — как-то спокойнее.

Короче, middleware — это как умный привратник в твоём приложении. Он может и пропустить, и не пустить, и записать кто приходил, и даже ответ немного подправить перед отправкой. Хуй с горы, а не инструмент!