Перейти к содержанию

Мидлвари

aliceio предоставляет мощный механизм настройки обработчиков событий через мидлвари.

Мидлвари здесь похожи на мидлвари в веб-фреймворках, таких как aiohttp, fastapi, Django и т.д. с небольшой разницей - здесь реализованы два уровня промежуточного программного обеспечения (до и после фильтров).

Примечание

Мидлварь - это код, который активируется при каждом событии, полученном от API Алисы.

Теория

Как говорится во многих книгах и другой литературе в Интернете:

Мидлвари — это повторно используемое программное обеспечение, которое использует шаблоны и платформы для устранения разрыва между функциональными требованиями приложений и базовыми операционными системами, стеками сетевых протоколов и базами данных

Мидлварь может изменять, расширять или отклонять событие обработки во многих местах конвейера (обработки).

База

Экземпляр мидлваря можно применить для каждого типа события (update, message, button_pressed etc) в двух местах:

  1. Внешняя (outer) область - перед обработкой фильтров (<router>.<event>.outer_middleware(...))
  2. Внутренняя (inner) область - после обработки фильтров, но перед обработчиком (<router>.<event>.middleware(...))

middleware_light.png middleware_dark.png

Визуализация порядка обработки

Внимание

Мидлварь должен быть подклассом BaseMiddleware (from aliceio inport BaseMiddleware) или поддерживать асинхронный __call__.

Практика

Важно

Мидлварь должен всегда вызывать await handler(event, data) чтобы передать событие следующему мидлварю/хэндлеру.
Если вы хотите завершить обработку события, вы должны не вызывать await handler(event, data)

Class-based

Напишем мидлварь, который будет "разворачивать" незалогиненных в Яндексе пользователей:

from aliceio import BaseMiddleware
from aliceio.types import Update

class UserAuthorizedMiddleware(BaseMiddleware[Update]):
    async def __call__(
        self,
        handler: Callable[[Update, dict[str, Any]], Awaitable[Any]],
        event: Update,
        data: dict[str, Any],
    ) -> Any:
        if event.session.user is None:
            logging.info("Замечен пользователь без аккаунта, блокирую!")
            return "Я вас не знаю, у вас нет аккаунта в Яндексе. А чтобы пользоваться мной, он нужен!"
        return await handler(event, data)

И теперь добавим его:

1
2
3
dp = Dispatcher()
dp.update.outer_middleware(UserAuthorizedMiddleware())
# либо `dp.update.middleware(UserAuthorizedMiddleware())`

Function-based

Напишем такой же мидлварь через функцию:

@dp.update.middleware()  # либо `@dp.update.outer_middleware()`
async def check_authorization(
    handler: Callable[[Update, dict[str, Any]], Awaitable[Any]],
    event: Update,
    data: dict[str, Any]
) -> Any:
    if event.session.user is None:
        logging.info("Замечен пользователь без аккаунта, блокирую!")
        return "Я вас не знаю, у вас нет аккаунта в Яндексе. А чтобы пользоваться мной, он нужен!"
    return await handler(event, data)

Факты

  1. Outer-мидлвари будут вызываться при каждом входящем событиию.
  2. Inner-мидлвари будут вызываться только при прохождении фильтров.
  3. Если вы ничего не вернёте из мидлваря, то Алиса завершит диалог с навыком.

Примеры