Hrvatski

Istražite napredne tehnike izmjene zahtjeva koristeći Next.js middleware. Naučite upravljati složenim usmjeravanjem, autentifikacijom, A/B testiranjem i strategijama lokalizacije za robusne web aplikacije.

Granični slučajevi Next.js Middlewarea: Ovladavanje uzorcima za izmjenu zahtjeva

Next.js middleware pruža moćan mehanizam za presretanje i izmjenu zahtjeva prije nego što stignu do ruta vaše aplikacije. Ova mogućnost otvara širok spektar prilika, od jednostavnih provjera autentifikacije do složenih scenarija A/B testiranja i strategija internacionalizacije. Međutim, učinkovito korištenje middlewarea zahtijeva duboko razumijevanje njegovih graničnih slučajeva i potencijalnih zamki. Ovaj sveobuhvatni vodič istražuje napredne uzorke za izmjenu zahtjeva, pružajući praktične primjere i korisne uvide koji će vam pomoći u izgradnji robusnih i performantnih Next.js aplikacija.

Razumijevanje osnova Next.js Middlewarea

Prije nego što zaronimo u napredne uzorke, ponovimo osnove Next.js middlewarea. Middleware funkcije se izvršavaju prije nego što se zahtjev dovrši, omogućujući vam da:

Middleware funkcije nalaze se u datoteci middleware.js ili middleware.ts u vašem /pages ili /app direktoriju (ovisno o vašoj verziji i postavkama Next.js-a). One primaju NextRequest objekt koji predstavlja dolazni zahtjev i mogu vratiti NextResponse objekt za kontrolu daljnjeg ponašanja.

Primjer: Osnovni middleware za autentifikaciju

Ovaj primjer demonstrira jednostavnu provjeru autentifikacije. Ako korisnik nije autentificiran (npr. nema valjanog tokena u kolačiću), preusmjerava se na stranicu za prijavu.


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*'],
}

Ovaj middleware će se izvršiti samo za rute koje odgovaraju /protected/:path*. Provjerava postojanje kolačića authToken. Ako kolačić nedostaje, korisnik se preusmjerava na stranicu /login. U suprotnom, zahtjev se normalno nastavlja pomoću NextResponse.next().

Napredni uzorci za izmjenu zahtjeva

Sada, istražimo neke napredne uzorke za izmjenu zahtjeva koji pokazuju pravu moć Next.js middlewarea.

1. A/B testiranje pomoću kolačića (Cookies)

A/B testiranje je ključna tehnika za optimizaciju korisničkog iskustva. Middleware se može koristiti za nasumično dodjeljivanje korisnika različitim varijacijama vaše aplikacije i praćenje njihovog ponašanja. Ovaj uzorak se oslanja na kolačiće kako bi se zadržala dodijeljena varijanta korisnika.

Primjer: A/B testiranje odredišne stranice


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) {
    // Randomly assign a 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: ['/'],
}

U ovom primjeru, kada korisnik prvi put posjeti korijensku putanju (/), middleware ga nasumično dodjeljuje ili variantA ili variantB. Ova varijanta se sprema u kolačić. Naknadni zahtjevi istog korisnika bit će prepisani na /variant-a ili /variant-b, ovisno o dodijeljenoj varijanti. To vam omogućuje posluživanje različitih odredišnih stranica i praćenje koja ima bolje rezultate. Osigurajte da imate definirane rute za /variant-a i /variant-b u svojoj Next.js aplikaciji.

Globalna razmatranja: Prilikom provođenja A/B testiranja, uzmite u obzir regionalne varijacije. Dizajn koji odjekuje u Sjevernoj Americi možda neće biti jednako učinkovit u Aziji. Možete koristiti geolokacijske podatke (dobivene putem IP adrese ili korisničkih preferencija) kako biste prilagodili A/B test specifičnim regijama.

2. Lokalizacija (i18n) s prepisivanjem URL-ova

Internacionalizacija (i18n) je ključna za dosezanje globalne publike. Middleware se može koristiti za automatsko otkrivanje preferiranog jezika korisnika i preusmjeravanje na odgovarajuću lokaliziranu verziju vaše stranice.

Primjer: Preusmjeravanje na temelju zaglavlja `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

  // Check if there's an existing locale in the 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).*)'
  ],
}

Ovaj middleware izvlači zaglavlje Accept-Language iz zahtjeva i određuje preferirani jezik korisnika. Ako URL već ne sadrži jezični prefiks (npr. /en/about), middleware preusmjerava korisnika na odgovarajući lokalizirani URL (npr. /fr/about za francuski). Pobrinite se da imate odgovarajuću strukturu mapa u svom /pages ili /app direktoriju za različite lokalizacije. Na primjer, trebat će vam datoteke /pages/en/about.js i /pages/fr/about.js.

Globalna razmatranja: Osigurajte da vaša i18n implementacija ispravno rukuje jezicima koji se pišu s desna na lijevo (npr. arapski, hebrejski). Također, razmislite o korištenju mreže za isporuku sadržaja (CDN) za posluživanje lokaliziranih resursa s poslužitelja bližih vašim korisnicima, poboljšavajući performanse.

3. Zastavice funkcionalnosti (Feature Flags)

Zastavice funkcionalnosti omogućuju vam da omogućite ili onemogućite značajke u svojoj aplikaciji bez implementacije novog koda. Ovo je posebno korisno za postupno uvođenje novih značajki ili za testiranje značajki u produkciji. Middleware se može koristiti za provjeru statusa zastavice funkcionalnosti i odgovarajuću izmjenu zahtjeva.

Primjer: Omogućavanje beta funkcionalnosti


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()
  }

  // Optionally redirect to a "feature unavailable" page
  return NextResponse.rewrite(new URL('/feature-unavailable', request.url))
}

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

Ovaj middleware provjerava vrijednost varijable okruženja BETA_FEATURE_ENABLED. Ako je postavljena na true i korisnik pokušava pristupiti ruti pod /new-feature, zahtjev se dopušta. U suprotnom, korisnik se preusmjerava na stranicu /feature-unavailable. Ne zaboravite odgovarajuće konfigurirati varijable okruženja za različita okruženja (razvojno, testno, produkcijsko).

Globalna razmatranja: Kada koristite zastavice funkcionalnosti, uzmite u obzir pravne implikacije omogućavanja značajki koje možda nisu u skladu s propisima u svim regijama. Na primjer, značajke povezane s privatnošću podataka možda će trebati biti onemogućene u određenim zemljama.

4. Detekcija uređaja i prilagodljivo usmjeravanje

Moderne web aplikacije moraju biti responzivne i prilagoditi se različitim veličinama zaslona i mogućnostima uređaja. Middleware se može koristiti za otkrivanje vrste uređaja korisnika i preusmjeravanje na optimizirane verzije vaše stranice.

Primjer: Preusmjeravanje mobilnih korisnika na poddomenu optimiziranu za mobilne uređaje


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: ['/'],
}

Ovaj primjer koristi biblioteku `detection` za određivanje vrste uređaja korisnika na temelju zaglavlja User-Agent. Ako je korisnik na mobilnom telefonu, preusmjerava se na poddomenu m.example.com (pod pretpostavkom da tamo imate verziju svoje stranice optimiziranu za mobilne uređaje). Ne zaboravite instalirati paket `detection`: `npm install detection`.

Globalna razmatranja: Osigurajte da vaša logika za detekciju uređaja uzima u obzir regionalne varijacije u korištenju uređaja. Na primjer, obični mobilni telefoni (feature phones) još su uvijek prisutni u nekim zemljama u razvoju. Razmislite o korištenju kombinacije detekcije User-Agenta i tehnika responzivnog dizajna za robusnije rješenje.

5. Obogaćivanje zaglavlja zahtjeva

Middleware može dodati informacije u zaglavlja zahtjeva prije nego što ih obrade rute vaše aplikacije. Ovo je korisno za dodavanje prilagođenih metapodataka, kao što su korisničke uloge, status autentifikacije ili ID-jevi zahtjeva, koje vaša aplikacijska logika može koristiti.

Primjer: Dodavanje ID-a zahtjeva


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*'], // Only apply to API routes
}

Ovaj middleware generira jedinstveni ID zahtjeva pomoću biblioteke uuid i dodaje ga u zaglavlje x-request-id. Ovaj ID se zatim može koristiti za bilježenje, praćenje i otklanjanje pogrešaka. Ne zaboravite instalirati paket `uuid`: `npm install uuid`.

Globalna razmatranja: Prilikom dodavanja prilagođenih zaglavlja, pazite na ograničenja veličine zaglavlja. Prekoračenje tih ograničenja može dovesti do neočekivanih pogrešaka. Također, osigurajte da su sve osjetljive informacije dodane u zaglavlja pravilno zaštićene, posebno ako je vaša aplikacija iza reverznog proxyja ili CDN-a.

6. Sigurnosna poboljšanja: Ograničavanje broja zahtjeva (Rate Limiting)

Middleware može djelovati kao prva linija obrane od zlonamjernih napada implementacijom ograničavanja broja zahtjeva. To sprječava zlouporabu ograničavanjem broja zahtjeva koje klijent može uputiti unutar određenog vremenskog okvira.

Primjer: Osnovno ograničavanje broja zahtjeva korištenjem jednostavnog spremišta


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

const requestCounts: { [ip: string]: number } = {}
const WINDOW_SIZE_MS = 60000; // 1 minute
const MAX_REQUESTS_PER_WINDOW = 100;

export function middleware(request: NextRequest) {
  const clientIP = request.ip || '127.0.0.1' // Get client IP, default to localhost for local testing

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

  requestCounts[clientIP]++;

  if (requestCounts[clientIP] > MAX_REQUESTS_PER_WINDOW) {
    return new NextResponse(
      JSON.stringify({ message: 'Too many requests' }),
      { status: 429, headers: { 'Content-Type': 'application/json' } }
    );
  }

  // Reset count after window
  setTimeout(() => {
    requestCounts[clientIP]--;
    if (requestCounts[clientIP] <= 0) {
        delete requestCounts[clientIP];
    }
  }, WINDOW_SIZE_MS);

  return NextResponse.next();
}

export const config = {
  matcher: ['/api/:path*'], // Apply to all API routes
}

Ovaj primjer održava jednostavno spremište u memoriji (requestCounts) za praćenje broja zahtjeva s svake IP adrese. Ako klijent premaši MAX_REQUESTS_PER_WINDOW unutar WINDOW_SIZE_MS, middleware vraća pogrešku 429 Too Many Requests. Važno: Ovo je pojednostavljen primjer i nije prikladan za produkcijska okruženja jer se ne skalira i ranjiv je na napade uskraćivanjem usluge (denial-of-service). Za produkcijsku upotrebu, razmislite o korištenju robusnijeg rješenja za ograničavanje broja zahtjeva poput Redisa ili namjenske usluge za to.

Globalna razmatranja: Strategije ograničavanja broja zahtjeva trebale bi biti prilagođene specifičnim karakteristikama vaše aplikacije i geografskoj distribuciji vaših korisnika. Razmislite o korištenju različitih ograničenja za različite regije ili segmente korisnika.

Granični slučajevi i potencijalne zamke

Iako je middleware moćan alat, ključno je biti svjestan njegovih ograničenja i potencijalnih zamki:

Najbolje prakse za korištenje Next.js Middlewarea

Da biste maksimalno iskoristili prednosti Next.js middlewarea i izbjegli potencijalne probleme, slijedite ove najbolje prakse:

Zaključak

Next.js middleware nudi moćan način za izmjenu zahtjeva i prilagodbu ponašanja vaše aplikacije na rubu mreže (edge). Razumijevanjem naprednih uzoraka za izmjenu zahtjeva o kojima se govori u ovom vodiču, možete izgraditi robusne, performantne i globalno svjesne Next.js aplikacije. Ne zaboravite pažljivo razmotriti granične slučajeve i potencijalne zamke te slijediti najbolje prakse navedene gore kako biste osigurali da su vaše middleware funkcije pouzdane i održive. Prihvatite moć middlewarea kako biste stvorili izvanredna korisnička iskustva i otključali nove mogućnosti za svoje web aplikacije.