В Universal Router middleware функции предоставляют мощный механизм для перехвата и обработки маршрутов до или после их разрешения. Они позволяют внедрять общую логику, такую как авторизация, логирование или модификация контекста, без необходимости дублирования кода в каждом маршруте.
Middleware в Universal Router реализуются через функцию
resolveRoute или цепочку вызовов внутри
resolve. Основная идея заключается в том, что каждый
middleware получает текущий контекст маршрута и функцию
next, которая передаёт управление следующему
обработчику.
const router = new UniversalRouter(routes, {
resolveRoute: async (context, next) => {
console.log('Middleware before route');
const result = await next();
console.log('Middleware after route');
return result;
}
});
Здесь context содержит всю информацию о текущем запросе,
включая путь, параметры, query и другие данные, передаваемые в маршруты.
Вызов next() позволяет передать управление следующему
middleware или самому маршруту.
Можно определить несколько middleware, которые будут выполняться последовательно. Порядок их определения критически важен, так как Universal Router обрабатывает их сверху вниз.
const logger = async (context, next) => {
console.log('Путь запроса:', context.pathname);
return next();
};
const auth = async (context, next) => {
if (!context.user) {
throw new Error('Неавторизованный доступ');
}
return next();
};
const router = new UniversalRouter(routes, {
resolveRoute: async (context, next) => {
return logger(context, () => auth(context, () => next()));
}
});
В этом примере сначала выполняется логгер, затем проверка авторизации, и только после этого — сам маршрут. Такая композиция позволяет строить гибкие цепочки обработки запросов.
context является ключевым объектом для middleware. Его
можно расширять, добавляя свойства, которые понадобятся в других
middleware или маршрутах.
const addRequestTime = async (context, next) => {
context.requestTime = Date.now();
return next();
};
const router = new UniversalRouter(routes, {
resolveRoute: async (context, next) => {
return addRequestTime(context, () => next());
}
});
После выполнения middleware context.requestTime будет
доступен в любом маршруте, что позволяет, например, логировать время
запроса или управлять кэшированием.
Middleware может использоваться для глобальной обработки ошибок. Если любая функция выбрасывает исключение, его можно поймать и обработать централизованно.
const errorHandler = async (context, next) => {
try {
return await next();
} catch (err) {
console.error('Ошибка маршрута:', err.message);
return { message: 'Произошла ошибка на сервере' };
}
};
const router = new UniversalRouter(routes, {
resolveRoute: async (context, next) => {
return errorHandler(context, () => next());
}
});
Таким образом можно обрабатывать ошибки в одном месте, не дублируя обработку в каждом маршруте.
Middleware идеально подходят для выполнения асинхронных операций, таких как загрузка данных с сервера, проверка прав доступа или выполнение внешних запросов.
const fetchUserData = async (context, next) => {
context.user = await getUserFromApi(context.params.userId);
return next();
};
Асинхронные middleware должны всегда возвращать результат вызова
next(), чтобы цепочка продолжалась корректно. Если
next() не вызывается, маршрут или последующие middleware не
будут выполнены.
Universal Router позволяет использовать middleware совместно с маршрутами, возвращающими промисы или функции. Это особенно удобно для SPA-приложений, где маршруты динамически подгружают компоненты или данные.
const routes = [
{
path: '/profile/:id',
async action(context) {
await fetchUserData(context, () => {});
return `Привет, ${context.user.name}`;
}
}
];
Даже внутри отдельного маршрута можно внедрять middleware для обработки специфических требований, что делает архитектуру приложения модульной и легко расширяемой.