Ответ
Для создания middleware в Python, как правило, реализуется класс с обязательными методами __init__ и __call__. Конкретная сигнатура этих методов зависит от спецификации, которой следует фреймворк (например, ASGI или WSGI).
Основной принцип middleware — это "обертка" над следующим обработчиком запроса, позволяющая выполнить код до и после его отработки.
Пример для Django (на основе WSGI)
В Django middleware инициализируется один раз и затем вызывается для каждого запроса.
class SimpleMiddleware:
def __init__(self, get_response):
# get_response — это следующий middleware в цепочке или само view
self.get_response = get_response
def __call__(self, request):
# Код, который выполняется до вызова view
print("Before request")
response = self.get_response(request)
# Код, который выполняется после вызова view
print("After request")
return response
Пример для FastAPI/Starlette (на основе ASGI)
ASGI-middleware работает с асинхронными вызовами и оперирует более низкоуровневыми объектами scope, receive, send.
from starlette.types import ASGIApp, Scope, Receive, Send
class CustomMiddleware:
def __init__(self, app: ASGIApp):
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send):
# Код до обработки запроса
if scope["type"] == "http":
# Можно модифицировать scope или заголовки
pass
await self.app(scope, receive, send)
# Код после обработки запроса (сложнее, т.к. ответ уже может быть отправлен)
Ключевые методы и их роли:
__init__(self, get_response / app): Конструктор. Вызывается один раз при запуске сервера для настройки middleware. Принимает следующий обработчик в цепочке.__call__(self, request / scope, receive, send): Основной метод. Вызывается для каждого запроса. Именно здесь реализуется логика обработки.
Ответ 18+ 🔞
А, ну вот, слушай, смотри, блядь. Middleware, сука, это как такой охранник на входе в клуб, понимаешь? Каждый, кто хочет зайти к тебе на view — сначала через него проходит, ёпта.
Вот смотри, в Python это обычно класс, да? И там два главных метода, без которых нихуя не работает. __init__ и __call__. Всё, как у людей, блядь.
__init__ — это типа сборка перед боем. Вызывается один раз, когда сервер запускается, и всё, пиздец. Ему говорят: «Вот, держи следующего пацана в цепочке (это get_response или app), он после тебя будет работать». И он его себе в карман кладёт, блядь, и ждёт.
А __call__ — это уже сам процесс, сука. Это когда конкретный клиент пришёл, и ты его начинаешь обрабатывать. Ты можешь с ним что-то сделать ДО того, как передашь его дальше, и ПОСЛЕ того, как он оттуда вернётся. Вот и вся магия, ёбана!
Теперь смотри, как это в разных бандах выглядит.
Для Django (этот старый, но проверенный WSGI-пахан)
Тут всё просто, как три копейки. Middleware получает get_response — это следующий мужик в очереди (или само view). А потом в __call__ приходит request, и ты его можешь пощупать.
class SimpleMiddleware:
def __init__(self, get_response):
# Запоминаем следующего в цепочке, типа "за мной, сука, очередь!"
self.get_response = get_response
def __call__(self, request):
# Тут ты делаешь что хочешь ДО того, как запрос уйдёт дальше
print("Щас запрос пойдёт, блядь!")
# А вот тут ты его передаёшь дальше по конвейеру
response = self.get_response(request)
# А тут ты можешь накостылить что-то ПОСЛЕ того, как ответ пришёл
print("Всё, запрос отгремел, можно и выпить!")
return response
Для FastAPI или Starlette (эти новые, асинхронные ASGI-шники)
Тут уже посложнее, потому что всё асинхронное, и работают они с какими-то scope, receive, send. Выглядит страшновато, но принцип тот же, блядь.
from starlette.types import ASGIApp, Scope, Receive, Send
class CustomMiddleware:
def __init__(self, app: ASGIApp):
# Опять же, цепляем следующее приложение
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send):
# Scope — это типа контекст запроса: кто, откуда, что хочет
# Тут можно его почитать или даже подправить, если ты такой хитрый
if scope["type"] == "http":
# Ну, например, проверить заголовки, блядь
pass
# Передаём управление дальше
await self.app(scope, receive, send)
# А вот тут, сука, осторожно! Ответ уже мог улететь клиенту,
# так что сильно потанцевать не получится. Но кое-что сделать можно.
Итог, ёпта:
__init__— настройка один раз при старте. Получаем ссылку на следующего.__call__— работа для каждого запроса. Делаем что-то до, пускаем запрос дальше, делаем что-то после.
Вот и вся философия, блядь. Ничего сложного, просто обёртка над основным обработчиком, как бутерброд вокруг колбасы. Главное — не переборщить с логикой, а то запрос нахуй так и не дойдёт, застрянет в твоих дебажных принтах, ебать!