Middleware¶
Middleware оборачивает каждый вызов хэндлера. Используй для логирования, rate limiting, проверки прав или внедрения общих объектов.
Как работает¶
Каждый middleware вызывает await call_next(event, data) чтобы передать управление дальше.
Написание middleware¶
from fastvk.middleware import BaseMiddleware
from fastvk.types import Message
class LoggingMiddleware(BaseMiddleware[Message]):
async def __call__(self, call_next, event: Message, data: dict) -> None:
print(f"[{event.peer_id}] {event.from_id}: {event.text}")
await call_next(event, data)
print(f"[{event.peer_id}] хэндлер завершён")
Регистрация middleware¶
bot = FastVK(token="...", group_id=123)
bot.message.middleware(LoggingMiddleware())
bot.callback.middleware(LoggingMiddleware())
Или на роутере:
Внедрение данных в хэндлеры¶
Middleware может добавлять значения в словарь data, который используется DI хэндлера:
class DatabaseMiddleware(BaseMiddleware[Message]):
def __init__(self, pool) -> None:
self.pool = pool
async def __call__(self, call_next, event: Message, data: dict) -> None:
async with self.pool.acquire() as conn:
data["db"] = conn # внедряется в хэндлер через DI
await call_next(event, data)
@bot.message()
async def handler(message: Message, db) -> None:
rows = await db.fetch("SELECT 1")
Пример rate limiting¶
import time
from collections import defaultdict
class RateLimitMiddleware(BaseMiddleware[Message]):
def __init__(self, limit: int = 5, window: int = 10) -> None:
self._counts: dict[int, list[float]] = defaultdict(list)
self._limit = limit
self._window = window
async def __call__(self, call_next, event: Message, data: dict) -> None:
now = time.monotonic()
user_id = event.from_id
times = [t for t in self._counts[user_id] if now - t < self._window]
self._counts[user_id] = times
if len(times) >= self._limit:
await event.answer("Слишком много запросов. Подожди.")
return
self._counts[user_id].append(now)
await call_next(event, data)