18. Middleware
Middleware в Astro — это функции, которые выполняются между получением запроса и отправкой ответа. Они позволяют централизованно обрабатывать аутентификацию, логирование, rate limiting, A/B-тестирование и многое другое.
Думай о middleware как о контрольно-пропускных пунктах на трассе: запрос проходит через каждый КПП по очереди, каждый может его проверить, изменить или остановить.
Создание middleware
Заголовок раздела «Создание middleware»import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(async (context, next) => { // context: { request, url, cookies, locals, redirect, ... }
// Код ПЕРЕД обработкой запроса console.log('→ Запрос:', context.request.url);
const response = await next(); // передаём управление дальше
// Код ПОСЛЕ обработки response.headers.set('X-Powered-By', 'Astro');
return response;});Объект context
Заголовок раздела «Объект context»export const onRequest = defineMiddleware(async (context, next) => { const { request, // Стандартный Web API Request url, // URL объект cookies, // AstroCookies — get/set/delete locals, // { } — передача данных между middleware и страницами redirect, // redirect(url, status) site, // базовый URL сайта } = context;
// Пишем в locals — доступно в .astro компонентах через Astro.locals context.locals.user = await getUser(request);
return next();});Аутентификационный middleware
Заголовок раздела «Аутентификационный middleware»export const onRequest = defineMiddleware(async ({ cookies, redirect, url, locals }, next) => { const protectedPaths = ['/dashboard', '/admin', '/profile']; const isProtected = protectedPaths.some(p => url.pathname.startsWith(p));
if (isProtected) { const token = cookies.get('session')?.value; if (!token) { return redirect('/login?from=' + url.pathname, 302); }
const user = await validateToken(token); if (!user) { cookies.delete('session'); return redirect('/login', 302); }
locals.user = user; }
return next();});Цепочка middleware с sequence()
Заголовок раздела «Цепочка middleware с sequence()»import { sequence } from 'astro:middleware';
// Логированиеconst logger = defineMiddleware(async ({ request }, next) => { const start = Date.now(); const response = await next(); console.log(\`\${request.method} \${request.url} → \${response.status} (\${Date.now() - start}ms)\`); return response;});
// Авторизацияconst auth = defineMiddleware(async ({ cookies, redirect }, next) => { if (!cookies.get('session')) return redirect('/login'); return next();});
// Rate limitingconst rateLimit = defineMiddleware(async ({ request }, next) => { const ip = request.headers.get('cf-connecting-ip') ?? 'unknown'; if (await isRateLimited(ip)) { return new Response('Too Many Requests', { status: 429 }); } return next();});
// Порядок выполнения: logger → rateLimit → auth → handlerexport const onRequest = sequence(logger, rateLimit, auth);Модификация запроса и ответа
Заголовок раздела «Модификация запроса и ответа»export const onRequest = defineMiddleware(async ({ request }, next) => { // Добавляем заголовок к запросу (нужно создать новый Request) const modifiedRequest = new Request(request, { headers: new Headers({ ...Object.fromEntries(request.headers), 'X-Request-ID': crypto.randomUUID(), }), });
const response = await next();
// Добавляем заголовки к ответу const modifiedResponse = new Response(response.body, response); modifiedResponse.headers.set('X-Response-Time', Date.now().toString()); modifiedResponse.headers.set('X-Frame-Options', 'DENY');
return modifiedResponse;});