Концепция промежуточных обработчиков

Промежуточные обработчики (middleware) в Page.js представляют собой функции, которые выполняются до основного обработчика маршрута и позволяют управлять потоком навигации, проверять условия доступа, модифицировать параметры запроса и подготавливать данные для последующего использования. Они обеспечивают гибкость и масштабируемость маршрутизации, особенно в приложениях с большим числом маршрутов.


Подключение промежуточных обработчиков

Промежуточные функции подключаются с помощью метода page() аналогично маршрутам, но без обязательного указания конечного обработчика:

page('/dashboard', checkAuth, loadUserData, showDashboard);

Здесь checkAuth и loadUserData — промежуточные обработчики, а showDashboard — конечный обработчик маршрута. Каждый промежуточный обработчик получает три аргумента:

function middleware(ctx, next) {
    // ctx — объект контекста маршрута
    // next — функция для передачи управления следующему обработчику
}
  • ctx — содержит параметры маршрута (ctx.params), query-параметры (ctx.querystring), а также ссылку на текущий URL (ctx.path) и другие данные.
  • next — функция, вызов которой передаёт управление следующему middleware или конечному обработчику. Если next() не вызывается, цепочка обработки останавливается.

Последовательность выполнения

Все промежуточные обработчики вызываются в порядке их объявления. Пример:

page('/profile/:id', checkAuth, fetchProfile, renderProfile);

function checkAuth(ctx, next) {
    if (!ctx.user) {
        page.redirect('/login');
    } else {
        next();
    }
}

function fetchProfile(ctx, next) {
    api.getUserProfile(ctx.params.id).then(profile => {
        ctx.profile = profile;
        next();
    });
}

function renderProfile(ctx) {
    document.body.innerHTML = `<h1>${ctx.profile.name}</h1>`;
}
  1. checkAuth проверяет авторизацию и при необходимости перенаправляет.
  2. fetchProfile получает данные пользователя и сохраняет их в ctx.
  3. renderProfile использует данные из ctx для отображения страницы.

Глобальные и локальные middleware

Глобальные middleware применяются ко всем маршрутам:

page('*', logNavigation);

function logNavigation(ctx, next) {
    console.log('Переход на:', ctx.path);
    next();
}
  • '*' означает, что обработчик сработает для любого маршрута.
  • Подходит для логирования, проверки сессии, установки общих параметров.

Локальные middleware подключаются к конкретному маршруту и выполняются только для него:

page('/admin', checkAdmin, renderAdmin);
  • Используются для проверки прав доступа к отдельным страницам или предварительной загрузки данных.

Асинхронные промежуточные обработчики

Page.js полностью поддерживает асинхронные операции через промисы или async/await:

page('/posts/:id', async function loadPost(ctx, next) {
    try {
        ctx.post = await api.getPost(ctx.params.id);
        next();
    } catch (err) {
        page.redirect('/error');
    }
});
  • Если next() не вызывается после завершения асинхронной операции, маршрут не продолжится.
  • Асинхронные middleware особенно полезны для загрузки данных, проверки авторизации через API или отложенной подгрузки компонентов.

Работа с параметрами и query

Middleware обеспечивает удобный доступ к параметрам маршрута и строке запроса:

page('/search/:term', parseQuery, showResults);

function parseQuery(ctx, next) {
    const params = new URLSearchParams(ctx.querystring);
    ctx.filters = params.get('filter') || 'all';
    next();
}
  • ctx.params.term содержит динамический сегмент URL.
  • ctx.querystring предоставляет доступ к GET-параметрам, которые можно обработать в middleware.

Управление потоком и перенаправления

Промежуточные обработчики могут останавливать цепочку или перенаправлять пользователя:

function requireLogin(ctx, next) {
    if (!ctx.user) {
        page.redirect('/login');
    } else {
        next();
    }
}
  • Если условие не выполнено, последующие middleware и конечный обработчик не вызываются.
  • Это позволяет централизованно управлять безопасностью и логикой доступа.

Комбинирование middleware

Page.js поддерживает цепочки middleware:

page('/dashboard', [checkAuth, fetchSettings, fetchNotifications], renderDashboard);
  • В массиве middleware порядок вызова сохраняется.
  • Позволяет создавать повторно используемые блоки логики, которые можно подключать к разным маршрутам.

Практические советы

  • Минимизировать дублирование: глобальные middleware для общих проверок (авторизация, логирование), локальные — для специфических маршрутов.
  • Использовать ctx для передачи данных: middleware могут обогащать контекст информацией, которая понадобится последующим обработчикам.
  • Останавливать цепочку при необходимости: next() вызывается только если дальнейшее выполнение маршрута допустимо.
  • Поддержка асинхронности: использовать async/await для API-запросов и загрузки ресурсов.
  • Композиция: объединять несколько middleware в массив для удобного подключения к маршруту.

Промежуточные обработчики в Page.js превращают маршрутизацию в мощный инструмент управления приложением, позволяя контролировать поток данных, права доступа и предварительную подготовку контента без дублирования кода.