Български

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

Next.js Middleware Крайни Случаи: Овладяване на Патерни за Модифициране на Заявки

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

Разбиране на Основите на Next.js Middleware

Преди да се потопим в усъвършенстваните патерни, нека си припомним основите на Next.js middleware. Middleware функциите се изпълняват преди да бъде завършена заявката, което ви позволява да:

Middleware функциите се намират във файла middleware.js или middleware.ts във вашата директория /pages или /app (в зависимост от вашата версия и настройка на Next.js). Те получават обект NextRequest, представляващ входящата заявка, и могат да върнат обект NextResponse за контрол на последващото поведение.

Пример: Основен middleware за удостоверяване

Този пример демонстрира проста проверка за удостоверяване. Ако потребителят не е удостоверен (напр. няма валиден токен в бисквитка), той се пренасочва към страницата за вход.


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const authToken = request.cookies.get('authToken')

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

  return NextResponse.next()
}

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

Този middleware ще се изпълнява само за маршрути, които съответстват на /protected/:path*. Той проверява за наличието на бисквитка authToken. Ако бисквитката липсва, потребителят се пренасочва към страницата /login. В противен случай, заявката се позволява да продължи нормално, използвайки NextResponse.next().

Усъвършенствани патерни за модифициране на заявки

Сега, нека разгледаме някои усъвършенствани патерни за модифициране на заявки, които демонстрират истинската мощ на Next.js middleware.

1. A/B Тестване с бисквитки

A/B тестването е решаваща техника за оптимизиране на потребителските изживявания. Middleware може да се използва за случайно присвояване на потребители на различни варианти на вашето приложение и проследяване на тяхното поведение. Този модел разчита на бисквитки, за да запази присвоения вариант на потребителя.

Пример: A/B тестване на целева страница


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const VARIANT_A = 'variantA'
const VARIANT_B = 'variantB'

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

  if (!variant) {
    // Случайно присвояване на вариант
    variant = Math.random() < 0.5 ? VARIANT_A : VARIANT_B
    const response = NextResponse.next()
    response.cookies.set('variant', variant)
    return response
  }

  if (variant === VARIANT_A) {
    return NextResponse.rewrite(new URL('/variant-a', request.url))
  } else if (variant === VARIANT_B) {
    return NextResponse.rewrite(new URL('/variant-b', request.url))
  }

  return NextResponse.next()
}

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

В този пример, когато потребител посети основния път (/) за първи път, middleware случайно им присвоява или variantA, или variantB. Този вариант се съхранява в бисквитка. Последващите заявки от същия потребител ще бъдат пренаписани или на /variant-a, или на /variant-b, в зависимост от присвоения им вариант. Това ви позволява да сервирате различни целеви страници и да проследявате коя работи по-добре. Уверете се, че имате дефинирани маршрути за /variant-a и /variant-b във вашето приложение Next.js.

Глобални съображения: Когато извършвате A/B тестване, обмислете регионалните вариации. Дизайн, който резонира в Северна Америка, може да не е толкова ефективен в Азия. Можете да използвате данни за геолокация (получени чрез търсене на IP адрес или потребителски предпочитания), за да адаптирате A/B теста към конкретни региони.

2. Локализация (i18n) с презаписване на URL адреси

Интернационализацията (i18n) е от съществено значение за достигане до глобална аудитория. Middleware може да се използва за автоматично откриване на предпочитания от потребителя език и пренасочването им към подходящата локализирана версия на вашия сайт.

Пример: Пренасочване въз основа на заглавката `Accept-Language`


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const SUPPORTED_LANGUAGES = ['en', 'fr', 'es', 'de']
const DEFAULT_LANGUAGE = 'en'

function getPreferredLanguage(request: NextRequest): string {
  const acceptLanguage = request.headers.get('accept-language')
  if (!acceptLanguage) {
    return DEFAULT_LANGUAGE
  }

  const languages = acceptLanguage.split(',').map((lang) => lang.split(';')[0].trim())

  for (const lang of languages) {
    if (SUPPORTED_LANGUAGES.includes(lang)) {
      return lang
    }
  }

  return DEFAULT_LANGUAGE
}

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname

  // Проверете дали има съществуваща локализация в пътя
  if (
    SUPPORTED_LANGUAGES.some(
      (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
    )
  ) {
    return NextResponse.next()
  }

  const preferredLanguage = getPreferredLanguage(request)

  return NextResponse.redirect(
    new URL(`/${preferredLanguage}${pathname}`, request.url)
  )
}

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico).*)'
  ],
}

Този middleware извлича заглавката Accept-Language от заявката и определя предпочитания от потребителя език. Ако URL адресът вече не съдържа езиков префикс (напр. /en/about), middleware пренасочва потребителя към подходящия локализиран URL (напр. /fr/about за френски език). Уверете се, че имате подходяща структура от папки в директорията си /pages или /app за различните локализации. Например, ще ви е необходим файл /pages/en/about.js и /pages/fr/about.js.

Глобални съображения: Уверете се, че вашата i18n имплементация обработва правилно езици от дясно на ляво (напр. арабски, иврит). Също така, помислете за използване на мрежа за доставка на съдържание (CDN), за да сервирате локализирани активи от сървъри, по-близо до вашите потребители, подобрявайки производителността.

3. Функционални флагове

Функционалните флагове ви позволяват да активирате или деактивирате функции във вашето приложение, без да разполагате нов код. Това е особено полезно за постепенно въвеждане на нови функции или за тестване на функции в производство. Middleware може да се използва за проверка на състоянието на функционалния флаг и съответно модифициране на заявката.

Пример: Активиране на Beta функция


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const BETA_FEATURE_ENABLED = process.env.BETA_FEATURE_ENABLED === 'true'

export function middleware(request: NextRequest) {
  if (BETA_FEATURE_ENABLED && request.nextUrl.pathname.startsWith('/new-feature')) {
    return NextResponse.next()
  }

  // По желание пренасочване към страница "функцията е недостъпна"
  return NextResponse.rewrite(new URL('/feature-unavailable', request.url))
}

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

Този middleware проверява стойността на променливата на средата BETA_FEATURE_ENABLED. Ако е зададена на true и потребителят се опитва да получи достъп до маршрут под /new-feature, заявката се разрешава да продължи. В противен случай потребителят се пренасочва към страница /feature-unavailable. Не забравяйте да конфигурирате правилно променливите на средата за различни среди (разработка, staging, производство).

Глобални съображения: Когато използвате функционални флагове, обмислете правните последствия от активирането на функции, които може да не отговарят на разпоредбите във всички региони. Например, функции, свързани със защитата на данните, може да трябва да бъдат деактивирани в определени страни.

4. Откриване на устройство и адаптивна маршрутизация

Съвременните уеб приложения трябва да бъдат отзивчиви и да се адаптират към различни размери на екрана и възможности на устройствата. Middleware може да се използва за откриване на типа устройство на потребителя и пренасочването им към оптимизирани версии на вашия сайт.

Пример: Пренасочване на мобилни потребители към мобилно оптимизиран поддомейн


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { device } from 'detection'

export function middleware(request: NextRequest) {
  const userAgent = request.headers.get('user-agent')

  if (userAgent) {
    const deviceType = device(userAgent)

    if (deviceType.type === 'phone') {
      const mobileUrl = new URL(request.url)
      mobileUrl.hostname = 'm.example.com'
      return NextResponse.redirect(mobileUrl)
    }
  }

  return NextResponse.next()
}

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

Този пример използва библиотеката `detection`, за да определи типа устройство на потребителя въз основа на заглавката User-Agent. Ако потребителят е на мобилен телефон, той се пренасочва към поддомейна m.example.com (като се предполага, че имате мобилно оптимизирана версия на вашия сайт, хоствана там). Не забравяйте да инсталирате пакета `detection`: `npm install detection`.

Глобални съображения: Уверете се, че вашата логика за откриване на устройство отчита регионалните вариации в употребата на устройства. Например, в някои развиващи се страни все още преобладават телефоните с функции. Обмислете използването на комбинация от откриване на User-Agent и техники за адаптивен дизайн за по-надеждно решение.

5. Обогатяване на заглавките на заявките

Middleware може да добавя информация към заглавките на заявките, преди те да бъдат обработени от маршрутите на вашето приложение. Това е полезно за добавяне на потребителски метаданни, като потребителски роли, състояние на удостоверяване или идентификационни номера на заявки, които могат да бъдат използвани от логиката на вашето приложение.

Пример: Добавяне на ID на заявка


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'

export function middleware(request: NextRequest) {
  const requestId = uuidv4()
  const response = NextResponse.next()
  response.headers.set('x-request-id', requestId)
  return response
}

export const config = {
  matcher: ['/api/:path*'], // Прилага се само за API маршрути
}

Този middleware генерира уникален ID на заявката, използвайки библиотеката uuid и го добавя към заглавката x-request-id. След това този ID може да се използва за регистриране, проследяване и отстраняване на грешки. Не забравяйте да инсталирате пакета uuid: `npm install uuid`.

Глобални съображения: Когато добавяте потребителски заглавки, имайте предвид ограниченията за размера на заглавките. Превишаването на тези ограничения може да доведе до неочаквани грешки. Също така, уверете се, че всяка чувствителна информация, добавена към заглавките, е правилно защитена, особено ако вашето приложение е зад обратен прокси или CDN.

6. Подобрения в сигурността: Ограничаване на скоростта

Middleware може да действа като първа линия на защита срещу злонамерени атаки чрез прилагане на ограничаване на скоростта. Това предотвратява злоупотребите, като ограничава броя на заявките, които клиентът може да направи в рамките на определен период от време.

Пример: Основно ограничаване на скоростта с помощта на просто хранилище


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const requestCounts: { [ip: string]: number } = {}
const WINDOW_SIZE_MS = 60000; // 1 минута
const MAX_REQUESTS_PER_WINDOW = 100;

export function middleware(request: NextRequest) {
  const clientIP = request.ip || '127.0.0.1' // Вземете IP адреса на клиента, по подразбиране localhost за локално тестване

  if (!requestCounts[clientIP]) {
    requestCounts[clientIP] = 0;
  }

  requestCounts[clientIP]++;

  if (requestCounts[clientIP] > MAX_REQUESTS_PER_WINDOW) {
    return new NextResponse(
      JSON.stringify({ message: 'Твърде много заявки' }),
      { status: 429, headers: { 'Content-Type': 'application/json' } }
    );
  }

  // Нулиране на броя след прозореца
  setTimeout(() => {
    requestCounts[clientIP]--;
    if (requestCounts[clientIP] <= 0) {
        delete requestCounts[clientIP];
    }
  }, WINDOW_SIZE_MS);

  return NextResponse.next();
}

export const config = {
  matcher: ['/api/:path*'], // Прилага се за всички API маршрути
}

Този пример поддържа просто хранилище в паметта (requestCounts), за да проследява броя на заявките от всеки IP адрес. Ако клиентът надвиши MAX_REQUESTS_PER_WINDOW в рамките на WINDOW_SIZE_MS, middleware връща грешка 429 Too Many Requests. Важно: Това е опростен пример и не е подходящ за производствени среди, тъй като не се мащабира и е уязвим към атаки за отказ от обслужване. За производствена употреба, помислете за използване на по-надеждно решение за ограничаване на скоростта като Redis или специална услуга за ограничаване на скоростта.

Глобални съображения: Стратегиите за ограничаване на скоростта трябва да бъдат приспособени към специфичните характеристики на вашето приложение и географското разпределение на вашите потребители. Обмислете използването на различни ограничения на скоростта за различни региони или потребителски сегменти.

Крайни случаи и потенциални недостатъци

Докато middleware е мощен инструмент, важно е да сте наясно с неговите ограничения и потенциални недостатъци:

Най-добри практики за използване на Next.js Middleware

За да увеличите максимално ползите от Next.js middleware и да избегнете потенциални проблеми, следвайте тези най-добри практики:

Заключение

Next.js middleware предлага мощен начин за модифициране на заявки и персонализиране на поведението на вашето приложение на ръба. Като разберете усъвършенстваните патерни за модифициране на заявки, обсъдени в това ръководство, можете да изградите надеждни, производителни и глобално осведомени Next.js приложения. Не забравяйте внимателно да обмислите крайните случаи и потенциалните недостатъци и да следвате най-добрите практики, описани по-горе, за да гарантирате, че вашите middleware функции са надеждни и подлежащи на поддръжка. Прегърнете силата на middleware, за да създадете изключителни потребителски изживявания и да отключите нови възможности за вашите уеб приложения.