Защита маршрутов с помощью middleware

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

В Iron обработка запроса организуется как последовательный pipeline. Каждый middleware выполняется в порядке регистрации и имеет возможность:

  • модифицировать контекст запроса
  • завершить обработку и вернуть ответ
  • передать управление следующему обработчику

Ключевым элементом является функция next, которая активирует следующий слой цепочки.

app.use(async (ctx, next) => {
  console.log("Запрос получен:", ctx.path);
  await next();
});

Если next() не вызывается, выполнение цепочки прерывается, и последующие middleware, включая конечный обработчик маршрута, не выполняются.

Контекст запроса и его расширение

Контекст в Iron служит единым объектом, содержащим данные запроса и ответа. Middleware часто используется для расширения этого контекста дополнительной информацией.

app.use(async (ctx, next) => {
  ctx.requestTime = Date.now();
  await next();
});

Такой подход позволяет последующим слоям использовать обогащённые данные без необходимости повторного вычисления.

Защита маршрутов через проверку авторизации

Одним из основных сценариев middleware является контроль доступа к маршрутам. Проверка токена или сессии выполняется до попадания в бизнес-логику.

async function authMiddleware(ctx, next) {
  const token = ctx.headers.authorization;

  if (!token) {
    ctx.status = 401;
    ctx.body = { error: "Отсутствует токен доступа" };
    return;
  }

  const user = verifyToken(token);

  if (!user) {
    ctx.status = 403;
    ctx.body = { error: "Недействительный токен" };
    return;
  }

  ctx.user = user;
  await next();
}

Middleware подобного типа завершает выполнение при отсутствии доступа, предотвращая выполнение защищённых маршрутов.

Разделение публичных и защищённых маршрутов

Архитектура Iron позволяет применять middleware как глобально, так и на уровне конкретных маршрутов. Это создаёт разделение между открытыми и закрытыми частями API.

app.use(publicRouter.routes());

app.use(authMiddleware);

app.use(privateRouter.routes());

В такой конфигурации все маршруты, зарегистрированные после authMiddleware, автоматически становятся защищёнными.

Ролевой доступ (Role-Based Access Control)

Расширение базовой авторизации заключается в проверке ролей пользователя. Middleware может анализировать свойства объекта пользователя и принимать решение о доступе.

function roleMiddleware(requiredRole) {
  return async (ctx, next) => {
    if (!ctx.user || ctx.user.role !== requiredRole) {
      ctx.status = 403;
      ctx.body = { error: "Недостаточно прав" };
      return;
    }

    await next();
  };
}

Использование цепочки middleware позволяет комбинировать проверки:

app.use(authMiddleware);
app.use(roleMiddleware("admin"));

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

Одной из сильных сторон Iron является возможность композиции middleware. Несколько функций могут быть объединены в единый поток обработки.

app.use(loggingMiddleware);
app.use(authMiddleware);
app.use(rateLimitMiddleware);
app.use(router.routes());

Порядок регистрации определяет порядок выполнения, что делает архитектуру предсказуемой и управляемой.

Перехват ошибок в middleware

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

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = 500;
    ctx.body = {
      error: "Внутренняя ошибка сервера"
    };
  }
});

Такой слой обеспечивает единообразную обработку исключений без необходимости дублирования логики в каждом маршруте.

Асинхронные операции в цепочке

Middleware в Iron поддерживает асинхронное выполнение, что критично для операций с базами данных, внешними API и файловой системой.

app.use(async (ctx, next) => {
  ctx.userProfile = await db.users.findById(ctx.user.id);
  await next();
});

Асинхронная модель позволяет сохранять линейную структуру кода при выполнении сложных операций.

Прерывание цепочки выполнения

Middleware может завершить обработку запроса на любом этапе. Это используется для кэширования, ограничения доступа или оптимизации.

app.use(async (ctx, next) => {
  const cached = cache.get(ctx.path);

  if (cached) {
    ctx.body = cached;
    return;
  }

  await next();
});

Прерывание цепочки снижает нагрузку на последующие слои системы.

Порядок регистрации и его влияние

Порядок подключения middleware определяет логику выполнения приложения. Ошибки в порядке могут привести к некорректной работе защиты маршрутов.

Типичный порядок:

  1. логирование
  2. обработка ошибок
  3. аутентификация
  4. авторизация
  5. маршрутизация
app.use(errorHandler);
app.use(logger);
app.use(authMiddleware);
app.use(router.routes());

Практика изоляции бизнес-логики

Middleware используется для отделения технической логики от бизнес-кода маршрутов. Это снижает связанность компонентов и упрощает сопровождение.

Маршрут остаётся минимальным:

router.get("/profile", async (ctx) => {
  ctx.body = ctx.userProfile;
});

Вся подготовка данных выполняется на уровне middleware.

Поток данных через middleware

Каждый middleware является звеном передачи данных. Контекст постепенно обогащается по мере прохождения цепочки:

  • входящий запрос
  • проверка безопасности
  • загрузка данных пользователя
  • применение бизнес-правил
  • формирование ответа

Такая модель позволяет строить сложные системы без дублирования логики.

Защита маршрутов как слой архитектуры

Middleware в Iron формирует отдельный архитектурный слой, отвечающий за безопасность. Этот слой отделён от маршрутов и бизнес-логики, что позволяет централизовать:

  • проверку токенов
  • контроль ролей
  • ограничение запросов
  • фильтрацию доступа

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