Добавление middleware через плагины

Fresh — это современный фреймворк для построения быстрых веб-приложений на основе Deno. Его ключевое отличие заключается в минимализме и высоком уровне производительности благодаря отсутствию сборщика и рендерингу на стороне сервера. Одной из важнейших возможностей является гибкая система middleware и плагинов, позволяющая расширять функциональность приложения без изменения его ядра.

Middleware в Fresh

Middleware — это функции, которые выполняются до обработки запроса конкретным маршрутом или после него, обеспечивая возможность управлять потоками данных, авторизацией, логированием, кэшированием и другими аспектами работы приложения. В Fresh middleware строятся вокруг объекта Context, который предоставляет доступ к запросу (Request) и ответу (Response), а также к состоянию приложения.

Основная структура middleware выглядит следующим образом:

export async function handler(req, ctx) {
  // Действия до передачи запроса дальше
  const result = await ctx.next(); // Передача запроса следующему middleware или обработчику
  // Действия после обработки запроса
  return result;
}

Ключевые моменты:

  • ctx.next() обязателен для передачи управления следующему уровню обработки.
  • Middleware можно использовать для асинхронных операций, таких как обращение к базе данных или внешним API.
  • Последовательность подключения middleware определяет порядок их выполнения.

Плагины в Fresh

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

Простейший пример плагина:

export default function myPlugin() {
  return {
    async onRequest(ctx) {
      console.log("Запрос получен:", ctx.req.url);
      return await ctx.next();
    },
  };
}

Особенности работы с плагинами:

  • Каждый плагин должен экспортировать функцию по умолчанию.
  • Внутри плагина можно определять события onRequest, onResponse или другие хуки.
  • Плагины могут использовать состояния (state) приложения для хранения данных между запросами.

Подключение плагинов

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

import { start } from "fresh/server.ts";
import myPlugin from "./plugins/myPlugin.js";

await start({
  plugins: [myPlugin()],
});

Важные моменты:

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

Создание собственного middleware через плагины

Middleware можно оформлять как отдельные функции и регистрировать их через плагины. Пример middleware для логирования времени выполнения запроса:

export function timingMiddleware() {
  return {
    async onRequest(ctx) {
      const start = performance.now();
      const result = await ctx.next();
      const duration = performance.now() - start;
      console.log(`Запрос ${ctx.req.url} обработан за ${duration.toFixed(2)}ms`);
      return result;
    },
  };
}

Подключение:

import { start } from "fresh/server.ts";
import { timingMiddleware } from "./plugins/timingMiddleware.js";

await start({
  plugins: [timingMiddleware()],
});

Управление состоянием через плагины

Плагины и middleware могут использовать глобальное или локальное состояние. Это удобно для кеширования, аутентификации или хранения пользовательских данных между запросами:

export function authPlugin() {
  return {
    async onRequest(ctx) {
      const token = ctx.req.headers.get("Authorization");
      ctx.state.user = token ? decodeToken(token) : null;
      return await ctx.next();
    },
  };
}

Порядок выполнения middleware и плагинов

  1. Все плагины регистрируются в порядке подключения.
  2. При поступлении запроса вызывается onRequest каждого middleware последовательно.
  3. После выполнения основного обработчика запроса вызываются onResponse хуки (если они определены).

Правильная организация порядка выполнения middleware критична для безопасности, логирования и производительности.

Ошибки и обработка исключений

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

export function errorHandler() {
  return {
    async onRequest(ctx) {
      try {
        return await ctx.next();
      } catch (err) {
        console.error("Ошибка в обработке запроса:", err);
        return new Response("Internal Server Error", { status: 500 });
      }
    },
  };
}

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

Итоговые принципы работы с Fresh middleware

  • Middleware всегда асинхронны и используют ctx.next() для передачи управления.
  • Плагины объединяют несколько middleware и предоставляют расширяемую архитектуру.
  • Порядок подключения плагинов и middleware определяет последовательность выполнения.
  • Состояние приложения (ctx.state) доступно для всех middleware и плагинов.
  • Обработка ошибок внутри middleware повышает устойчивость приложения.

Эти принципы обеспечивают масштабируемость, высокую производительность и модульность приложений на Fresh.