Перейти к содержимому

18. Middleware

Middleware в Astro — это функции, которые выполняются между получением запроса и отправкой ответа. Они позволяют централизованно обрабатывать аутентификацию, логирование, rate limiting, A/B-тестирование и многое другое.

Думай о middleware как о контрольно-пропускных пунктах на трассе: запрос проходит через каждый КПП по очереди, каждый может его проверить, изменить или остановить.


src/middleware.ts
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;
});

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();
});

src/middleware.ts
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();
});

src/middleware.ts
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 limiting
const 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 → handler
export 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;
});