Русский

Изучите middleware в Next.js — мощную функцию для перехвата и изменения входящих запросов. Узнайте, как реализовать аутентификацию, авторизацию, перенаправление и A/B-тестирование на практических примерах.

Middleware в Next.js: Освоение перехвата запросов для динамических приложений

Middleware в Next.js предоставляет гибкий и мощный способ перехватывать и изменять входящие запросы до того, как они достигнут ваших маршрутов. Эта возможность позволяет реализовывать широкий спектр функций, от аутентификации и авторизации до перенаправления и A/B-тестирования, при этом оптимизируя производительность. Это всеобъемлющее руководство проведет вас через основные концепции middleware в Next.js и покажет, как эффективно его использовать.

Что такое Middleware в Next.js?

Middleware в Next.js — это функция, которая выполняется до завершения обработки запроса. Она позволяет вам:

Функции Middleware определяются в файле middleware.ts (или middleware.js) в корне вашего проекта. Они выполняются для каждого маршрута в вашем приложении или для определенных маршрутов на основе настраиваемых сопоставителей (matchers).

Ключевые концепции и преимущества

Объект Request

Объект request предоставляет доступ к информации о входящем запросе, включая:

Объект Response

Функции Middleware возвращают объект Response для управления результатом запроса. Вы можете использовать следующие ответы:

Сопоставители (Matchers)

Сопоставители позволяют указать, к каким маршрутам должно применяться ваше middleware. Вы можете определять сопоставители с помощью регулярных выражений или шаблонов путей. Это гарантирует, что ваше middleware выполняется только при необходимости, улучшая производительность и снижая накладные расходы.

Edge Runtime

Middleware в Next.js работает в среде Edge Runtime, которая является легковесной средой выполнения JavaScript, развертываемой близко к вашим пользователям. Эта близость минимизирует задержку и улучшает общую производительность вашего приложения, особенно для глобально распределенных пользователей. Edge Runtime доступна в сети Vercel Edge Network и на других совместимых платформах. Edge Runtime имеет некоторые ограничения, в частности, на использование API Node.js.

Практические примеры: Реализация функций Middleware

1. Аутентификация

Middleware для аутентификации можно использовать для защиты маршрутов, требующих входа пользователей в систему. Вот пример реализации аутентификации с использованием cookie:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

Это middleware проверяет наличие cookie auth_token. Если cookie не найден, пользователь перенаправляется на страницу /login. config.matcher указывает, что это middleware должно выполняться только для маршрутов в разделе /dashboard.

Глобальная перспектива: Адаптируйте логику аутентификации для поддержки различных методов (например, OAuth, JWT) и интегрируйте с различными поставщиками удостоверений (например, Google, Facebook, Azure AD), чтобы обслуживать пользователей из разных регионов.

2. Авторизация

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


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Пример: Получение ролей пользователя из API (замените на вашу реальную логику)
 const userResponse = await fetch('https://api.example.com/userinfo', {
 headers: {
 Authorization: `Bearer ${token}`,
 },
 });
 const userData = await userResponse.json();

 if (userData.role !== 'admin') {
 return NextResponse.redirect(new URL('/unauthorized', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/admin/:path*'],
}

Это middleware получает роль пользователя и проверяет, есть ли у него роль admin. Если нет, он перенаправляется на страницу /unauthorized. В этом примере используется заглушка для конечной точки API. Замените `https://api.example.com/userinfo` на реальную конечную точку вашего сервера аутентификации.

Глобальная перспектива: Помните о правилах конфиденциальности данных (например, GDPR, CCPA) при обработке данных пользователей. Внедряйте соответствующие меры безопасности для защиты конфиденциальной информации и обеспечения соответствия местному законодательству.

3. Перенаправление

Middleware для перенаправления можно использовать для перенаправления пользователей в зависимости от их местоположения, языка или других критериев. Например, вы можете перенаправить пользователей на локализованную версию вашего сайта на основе их IP-адреса.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const country = request.geo?.country || 'US'; // По умолчанию US, если геолокация не удалась

 if (country === 'DE') {
 return NextResponse.redirect(new URL('/de', request.url))
 }

 if (country === 'FR') {
 return NextResponse.redirect(new URL('/fr', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/'],
}

Это middleware проверяет страну пользователя по его IP-адресу и перенаправляет на соответствующую локализованную версию сайта (/de для Германии, /fr для Франции). Если геолокация не удается, по умолчанию используется версия для США. Обратите внимание, что это зависит от доступности свойства `geo` (например, при развертывании на Vercel).

Глобальная перспектива: Убедитесь, что ваш сайт поддерживает несколько языков и валют. Предоставьте пользователям возможность вручную выбирать предпочитаемый язык или регион. Используйте соответствующие форматы даты и времени для каждой локали.

4. A/B-тестирование

Middleware можно использовать для реализации A/B-тестирования, случайным образом распределяя пользователей по разным вариантам страницы и отслеживая их поведение. Вот упрощенный пример:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

function getRandomVariant() {
 return Math.random() < 0.5 ? 'A' : 'B';
}

export function middleware(request: NextRequest) {
 let variant = request.cookies.get('variant')?.value;

 if (!variant) {
 variant = getRandomVariant();
 const response = NextResponse.next();
 response.cookies.set('variant', variant);
 return response;
 }

 if (variant === 'B') {
 return NextResponse.rewrite(new URL('/variant-b', request.url));
 }

 return NextResponse.next();
}

export const config = {
 matcher: ['/'],
}

Это middleware назначает пользователей либо варианту 'A', либо 'B'. Если у пользователя еще нет cookie variant, он назначается случайным образом и устанавливается. Пользователи, назначенные варианту 'B', перенаправляются на страницу /variant-b с помощью rewrite. Затем вы будете отслеживать производительность каждого варианта, чтобы определить, какой из них более эффективен.

Глобальная перспектива: Учитывайте культурные различия при разработке A/B-тестов. То, что хорошо работает в одном регионе, может не найти отклика у пользователей в другом. Убедитесь, что ваша платформа для A/B-тестирования соответствует нормам конфиденциальности в разных регионах.

5. Флаги функций (Feature Flags)

Флаги функций позволяют включать или отключать функции в вашем приложении без развертывания нового кода. Middleware можно использовать для определения, должен ли пользователь иметь доступ к определенной функции на основе его ID, местоположения или других критериев.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 // Пример: Получение флагов функций из API
 const featureFlagsResponse = await fetch('https://api.example.com/featureflags', {
 headers: {
 'X-User-Id': 'user123',
 },
 });
 const featureFlags = await featureFlagsResponse.json();

 if (featureFlags.new_feature_enabled) {
 // Включить новую функцию
 return NextResponse.next();
 } else {
 // Отключить новую функцию (например, перенаправить на альтернативную страницу)
 return NextResponse.redirect(new URL('/alternative-page', request.url));
 }
}

export const config = {
 matcher: ['/new-feature'],
}

Это middleware получает флаги функций из API и проверяет, установлен ли флаг new_feature_enabled. Если да, пользователь может получить доступ к странице /new-feature. В противном случае он перенаправляется на /alternative-page.

Глобальная перспектива: Используйте флаги функций для постепенного развертывания новых функций для пользователей в разных регионах. Это позволяет отслеживать производительность и устранять любые проблемы до выпуска функции для более широкой аудитории. Также убедитесь, что ваша система флагов функций масштабируется глобально и обеспечивает согласованные результаты независимо от местоположения пользователя. Учитывайте региональные нормативные ограничения для развертывания функций.

Продвинутые техники

Цепочки Middleware

Вы можете объединять несколько функций middleware в цепочку для выполнения серии операций над запросом. Это может быть полезно для разделения сложной логики на более мелкие, управляемые модули.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const response = NextResponse.next();

 // Первая функция middleware
 const token = request.cookies.get('auth_token');
 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Вторая функция middleware
 response.headers.set('x-middleware-custom', 'value');

 return response;
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

Этот пример показывает два middleware в одном. Первое выполняет аутентификацию, а второе устанавливает пользовательский заголовок.

Использование переменных окружения

Храните конфиденциальную информацию, такую как ключи API и учетные данные базы данных, в переменных окружения, а не вшивайте их в код ваших функций middleware. Это повышает безопасность и упрощает управление конфигурацией вашего приложения.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const API_KEY = process.env.API_KEY;

export async function middleware(request: NextRequest) {
 const response = await fetch('https://api.example.com/data', {
 headers: {
 'X-API-Key': API_KEY,
 },
 });

 // ...
}

export const config = {
 matcher: ['/data'],
}

В этом примере API_KEY извлекается из переменной окружения.

Обработка ошибок

Реализуйте надежную обработку ошибок в ваших функциях middleware, чтобы предотвратить сбой приложения из-за непредвиденных ошибок. Используйте блоки try...catch для перехвата исключений и соответствующего логирования ошибок.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 try {
 const response = await fetch('https://api.example.com/data');
 // ...
 } catch (error) {
 console.error('Ошибка при получении данных:', error);
 return NextResponse.error(); // Или перенаправить на страницу ошибки
 }
}

export const config = {
 matcher: ['/data'],
}

Лучшие практики

Устранение распространенных проблем

Заключение

Middleware в Next.js — это мощный инструмент для создания динамичных и персонализированных веб-приложений. Освоив перехват запросов, вы сможете реализовать широкий спектр функций, от аутентификации и авторизации до перенаправления и A/B-тестирования. Следуя лучшим практикам, изложенным в этом руководстве, вы сможете использовать middleware в Next.js для создания высокопроизводительных, безопасных и масштабируемых приложений, отвечающих потребностям вашей глобальной пользовательской базы. Воспользуйтесь мощью middleware, чтобы открыть новые возможности в ваших проектах на Next.js и обеспечить исключительный пользовательский опыт.