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

Мидлвары

Мидлвар позволяет перехватывать запросы и ответы, а также динамически внедрять поведения каждый раз, когда страница или конечная точка собирается быть отрисованной. Эта отрисовка происходит во время сборки для всех предварительно отрисованных страниц, но происходит, когда запрашивается маршрут для страниц, отрисовываемых по запросу, что делает доступными дополнительные функции SSR, такие как куки и заголовки (EN).

Мидлвар также позволяет устанавливать и делиться информацией, специфичной для запроса, между конечными точками и страницами, изменяя объект locals, который доступен во всех компонентах Astro и API эндпойнтов. Этот объект доступен даже когда этот мидлвар выполняется во время сборки.

  1. Создайте src/middleware.js|ts (или src/middleware/index.js|ts).

  2. Внутри этого файла экспортируйте функцию onRequest() (EN), которой можно передать объект context и функцию next(). Это не должен быть экспорт по умолчанию.

    src/middleware.js
    export function onRequest ({ locals, request }, next) {
    // перехват данных из запроса
    // меняем свойства в `locals`, если нужно
    locals.title = "Новый заголовок";
    // возвращаем ответ или результат вызова `next()`
    return next();
    };
  3. В любом файле .astro получите данные ответа, используя Astro.locals.

    src/components/Component.astro
    ---
    const data = Astro.locals;
    ---
    <h1>{data.title}</h1>
    <p>Это {data.property} из мидлвара.</p>

Объект context содержит информацию, которая будет доступна другому мидлвару, маршрутам API и маршрутам .astro в процессе рендеринга.

Это необязательный аргумент, передаваемый в onRequest(), который может содержать объект locals, а также любые дополнительные свойства, которые будут переданы во время рендеринга. Например, объект context может содержать куки, используемые при аутентификации.

context.locals — это объект, которым можно манипулировать внутри мидлвара.

Этот объект locals передается в процессе обработки запроса и доступен в качестве свойства для APIContext и AstroGlobal. Это позволяет обмениваться данными между мидлваром, маршрутами API и страницами .astro. Это полезно для хранения специфических данных запроса, таких как данные пользователя, на всех этапах рендеринга.

Внутри locals можно хранить любые типы данных: строки, числа и даже сложные типы данных, такие как функции и карты.

src/middleware.js
export function onRequest ({ locals, request }, next) {
// перехват данных из запроса
// меняем свойства в `locals`, если нужно
locals.user.name = "Джон Уик";
locals.welcomeTitle = () => {
return "С возвращением, " + locals.user.name;
};
// возвращаем ответ или результат вызова `next()`
return next();
};

Затем вы можете использовать эту информацию внутри любого файла .astro с помощью Astro.locals.

src/pages/orders.astro
---
const title = Astro.locals.welcomeTitle();
const orders = Array.from(Astro.locals.orders.entries());
const data = Astro.locals;
---
<h1>{title}</h1>
<p>Это {data.property} из мидлвара.</p>
<ul>
{orders.map(order => {
return <li>{/* делаем что-нибудь с каждым заказом */}</li>;
})}
</ul>

locals — это объект, который живет и умирает в рамках одного маршрута Astro; когда страница вашего маршрута будет отрисована, locals перестанет существовать и будет создан новый. Информация, которая должна сохраняться в течение нескольких запросов страницы, должна храниться в другом месте.

Пример: редактирование конфиденциальной информации

Заголовок раздела Пример: редактирование конфиденциальной информации

В примере ниже используется мидлвар для замены слова «PRIVATE INFO» на слово «REDACTED», что позволяет отображать изменённый HTML на вашей странице:

src/middleware.js
export const onRequest = async (context, next) => {
const response = await next();
const html = await response.text();
const redactedHtml = html.replaceAll("PRIVATE INFO", "REDACTED");
return new Response(redactedHtml, {
status: 200,
headers: response.headers
});
};

Можно импортировать и использовать служебную функцию defineMiddleware(), чтобы пользоваться преимуществами типобезопасности:

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
// `context` и `next` автоматически типизируются
export const onRequest = defineMiddleware((context, next) => {
});

В противном случае, если вы используете JsDoc для обеспечения типобезопасности, вы можете использовать MiddlewareHandler:

src/middleware.js
/**
* @type {import("astro").MiddlewareHandler}
*/
// `context` и `next` автоматически типизируются
export const onRequest = (context, next) => {
};

Чтобы типизировать информацию внутри Astro.locals, что дает вам автозаполнение внутри файлов .astro и кода мидлвара, объявите глобальное пространство имён в файле env.d.ts:

src/env.d.ts
declare namespace App {
interface Locals {
user: {
name: string
},
welcomeTitle: () => string,
orders: Map<string, object>
}
}

Затем, внутри файла мидлвара, вы можете воспользоваться преимуществами автозавершения и типобезопасности.

Несколько мидлваров могут быть объединены в определённом порядке с помощью sequence() (EN):

src/middleware.js
import { sequence } from "astro:middleware";
async function validation(_, next) {
console.log("validation request");
const response = await next();
console.log("validation response");
return response;
}
async function auth(_, next) {
console.log("auth request");
const response = await next();
console.log("auth response");
return response;
}
async function greeting(_, next) {
console.log("greeting request");
const response = await next();
console.log("greeting response");
return response;
}
export const onRequest = sequence(validation, auth, greeting);

Это приведет к следующему порядку в консоли:

Окно терминала
validation request
auth request
greeting request
greeting response
auth response
validation response

Добавлено в: astro@4.13.0

APIContext предоставляет метод rewrite(), который работает так же, как и Astro.rewrite.

Используйте context.rewrite() внутри мидлвара, чтобы отобразить содержимое другой страницы без перенаправления вашего посетителя на новую страницу. Это вызовет новую фазу отрисовки, что приведет к повторному выполнению любого мидлвара.

src/middleware.js
import { isLoggedIn } from "~/auth.js"
export function onRequest (context, next) {
if (!isLoggedIn(context)) {
// Если пользователь не вошел в систему, обновляем запрос, чтобы отобразить маршрут `/login` и
// добавляем заголовок, чтобы указать, куда пользователь должен быть отправлен после успешного входа.
// Повторно выполняем мидлвар.
return context.rewrite(new Request("/login", {
headers: {
"x-redirect-to": context.url.pathname
}
}));
}
return next();
};

Вы также можете передать функции next() необязательный параметр URL-пути, чтобы переписать текущий Request без повторного запуска новой фазы отрисовки. Местоположение пути переписывания может быть указано в виде строки, URL или Request:

src/middleware.js
import { isLoggedIn } from "~/auth.js"
export function onRequest (context, next) {
if (!isLoggedIn(context)) {
// Если пользователь не вошел в систему, обновляем запрос, чтобы отобразить маршрут `/login` и
// добавляем заголовок, чтобы указать, куда пользователь должен быть отправлен после успешного входа.
// Возвращаем новый `context` для любых последующих мидлваров.
return next(new Request("/login", {
headers: {
"x-redirect-to": context.url.pathname
}
}));
}
return next();
};

Функция next() принимает тот же набор параметров, что и функция Astro.rewrite(). Местоположение пути переписывания может быть указано в виде строки, URL или Request.

Когда у вас есть несколько функций мидлвара, связанных через sequence(), передача пути в next() перепишет Request на месте, и мидлвар не будет выполнен снова. Следующая функция мидлвара в цепочке получит новый Request с обновлённым context:

Вызов next() с этой сигнатурой создаст новый объект Request, используя старый ctx.request. Это означает, что попытка получить доступ к Request.body, как до, так и после этого переписывания, вызовет ошибку во время выполнения. Эта ошибка часто возникает при использовании Astro Actions, которые используют HTML-формы (EN). В этих случаях мы рекомендуем обрабатывать переписывания из ваших шаблонов Astro, используя Astro.rewrite(), вместо использования мидлвара.

src/middleware.js
// Текущий URL-адрес https://example.com/blog
// Первая функция мидлвара
async function first(_, next) {
console.log(context.url.pathname) // отобразит "/blog"
// Перезаписываем новый маршрут, домашнюю страницу
// Возвращаем обновлённый `context`, который передается функции next
return next("/")
}
// Текущий URL-адрес всё ещё https://example.com/blog
// Вторая функция мидлвара
async function second(context, next) {
// Получает обновлённый `context`
console.log(context.url.pathname) // отобразит "/"
return next()
}
export const onRequest = sequence(first, second);

Мидлвар будет пытаться запустить все страницы по требованию, даже если подходящий маршрут не может быть найден. Это включает в себя стандартную (пустую) страницу 404 в Astro и любые пользовательские страницы 404. Однако решение о том, будет ли выполняться этот код, зависит от адаптера (EN). Некоторые адаптеры могут выдавать вместо этого страницу ошибки, специфичную для конкретной платформы.

Мидлвар также попытается запуститься перед выдачей страницы ошибки 500, включая пользовательскую страницу 500, если только ошибка сервера не произошла во время выполнения самого мидлвара. Если ваш мидлвар не будет успешно запущен, то у вас не будет доступа к Astro.locals для отображения вашей 500-страницы.

Внести свой вклад Сообщество Sponsor