Дослідіть middleware у Next.js, потужну функцію для перехоплення та модифікації вхідних запитів. Дізнайтеся, як реалізувати автентифікацію, авторизацію, редиректи та A/B-тестування на практичних прикладах.
Middleware у Next.js: Майстерне перехоплення запитів для динамічних додатків
Middleware у Next.js надає гнучкий та потужний спосіб перехоплювати та модифікувати вхідні запити перед тим, як вони досягнуть ваших маршрутів. Ця можливість дозволяє реалізувати широкий спектр функцій, від автентифікації та авторизації до редиректів та A/B-тестування, оптимізуючи при цьому продуктивність. Цей вичерпний посібник проведе вас через основні концепції middleware у Next.js та продемонструє, як його ефективно використовувати.
Що таке Middleware у Next.js?
Middleware у Next.js — це функція, яка виконується до того, як запит буде завершено. Вона дозволяє вам:
- Перехоплювати запити: Аналізувати заголовки, файли cookie та URL-адресу вхідного запиту.
- Модифікувати запити: Переписувати URL-адреси, встановлювати заголовки або перенаправляти користувачів на основі певних критеріїв.
- Виконувати код: Запускати логіку на стороні сервера перед рендерингом сторінки.
Функції middleware визначаються у файлі middleware.ts
(або middleware.js
) у кореневому каталозі вашого проєкту. Вони виконуються для кожного маршруту у вашому додатку або для конкретних маршрутів на основі конфігурованих матчерів (matchers).
Ключові концепції та переваги
Об'єкт Request
Об'єкт request
надає доступ до інформації про вхідний запит, включаючи:
request.url
: Повна URL-адреса запиту.request.method
: HTTP-метод (наприклад, GET, POST).request.headers
: Об'єкт, що містить заголовки запиту.request.cookies
: Об'єкт, що представляє файли cookie запиту.request.geo
: Надає дані геолокації, пов'язані із запитом, якщо вони доступні.
Об'єкт Response
Функції middleware повертають об'єкт Response
для контролю результату запиту. Ви можете використовувати такі відповіді:
NextResponse.next()
: Продовжує обробку запиту у звичайному режимі, дозволяючи йому досягти призначеного маршруту.NextResponse.redirect(url)
: Перенаправляє користувача на іншу URL-адресу.NextResponse.rewrite(url)
: Переписує URL-адресу запиту, фактично обслуговуючи іншу сторінку без редиректу. URL-адреса в браузері залишається незмінною.- Повернення власного об'єкта
Response
: Дозволяє вам надавати власний контент, такий як сторінка помилки або конкретна JSON-відповідь.
Матчери (Matchers)
Матчери дозволяють вказати, до яких маршрутів має застосовуватися ваше middleware. Ви можете визначати матчери за допомогою регулярних виразів або шаблонів шляхів. Це гарантує, що ваше middleware запускається лише за потреби, покращуючи продуктивність та зменшуючи навантаження.
Edge Runtime
Middleware у Next.js працює на Edge Runtime, яке є полегшеним середовищем виконання JavaScript, що може бути розгорнуте близько до ваших користувачів. Ця близькість мінімізує затримку та покращує загальну продуктивність вашого додатка, особливо для глобально розподілених користувачів. Edge Runtime доступне в Edge Network від Vercel та на інших сумісних платформах. Edge Runtime має деякі обмеження, зокрема щодо використання Node.js API.
Практичні приклади: Реалізація функцій 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))
}
// Example: Fetch user roles from an API (replace with your actual logic)
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'; // Default to US if geo-location fails
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
. Потім ви відстежуєте продуктивність кожного варіанту, щоб визначити, який з них ефективніший.
Глобальна перспектива: Враховуйте культурні відмінності при розробці 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) {
// Example: Fetch feature flags from an 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) {
// Enable the new feature
return NextResponse.next();
} else {
// Disable the new feature (e.g., redirect to an alternative page)
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();
// First middleware function
const token = request.cookies.get('auth_token');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
// Second middleware function
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 fetching data:', error);
return NextResponse.error(); // Or redirect to an error page
}
}
export const config = {
matcher: ['/data'],
}
Найкращі практики
- Зберігайте функції middleware легкими: Уникайте виконання обчислювально інтенсивних операцій у middleware, оскільки це може вплинути на продуктивність. Переносьте складну обробку на фонові завдання або спеціалізовані сервіси.
- Ефективно використовуйте матчери: Застосовуйте middleware лише до тих маршрутів, які цього вимагають.
- Ретельно тестуйте ваше middleware: Пишіть юніт-тести, щоб переконатися, що ваші функції middleware працюють правильно.
- Моніторте продуктивність middleware: Використовуйте інструменти моніторингу для відстеження продуктивності ваших функцій middleware та виявлення будь-яких вузьких місць.
- Документуйте ваше middleware: Чітко документуйте мету та функціональність кожної функції middleware.
- Враховуйте обмеження Edge Runtime: Будьте в курсі обмежень Edge Runtime, таких як відсутність Node.js API. Відповідно коригуйте свій код.
Вирішення поширених проблем
- Middleware не запускається: Двічі перевірте конфігурацію матчера, щоб переконатися, що middleware застосовується до правильних маршрутів.
- Проблеми з продуктивністю: Виявляйте та оптимізуйте повільні функції middleware. Використовуйте інструменти профілювання для виявлення вузьких місць у продуктивності.
- Сумісність з Edge Runtime: Переконайтеся, що ваш код сумісний з Edge Runtime. Уникайте використання Node.js API, які не підтримуються.
- Проблеми з файлами cookie: Перевірте, чи правильно встановлюються та отримуються файли cookie. Звертайте увагу на атрибути cookie, такі як
domain
,path
таsecure
. - Конфлікти заголовків: Будьте в курсі можливих конфліктів заголовків при встановленні власних заголовків у middleware. Переконайтеся, що ваші заголовки не перезаписують існуючі заголовки ненавмисно.
Висновок
Middleware у Next.js — це потужний інструмент для створення динамічних та персоналізованих веб-додатків. Опанувавши перехоплення запитів, ви можете реалізувати широкий спектр функцій, від автентифікації та авторизації до редиректів та A/B-тестування. Дотримуючись найкращих практик, викладених у цьому посібнику, ви зможете використовувати middleware Next.js для створення високопродуктивних, безпечних та масштабованих додатків, що відповідають потребам вашої глобальної бази користувачів. Використовуйте потужність middleware, щоб відкрити нові можливості у ваших проєктах на Next.js та забезпечити винятковий досвід користувачів.