Middleware
middleware.ts: przekierowania, przepisywanie adresów, nagłówki i ciasteczka przed każdym żądaniem, z config.matcher.
Middleware uruchamia kod przed zakończeniem żądania. Może przekierowywać, przepisywać adresy, ustawiać nagłówki i pliki cookie lub samodzielnie zwracać odpowiedź.
Konwencja
Utwórz middleware.ts w katalogu głównym projektu (lub w src/), obok app/. Wyeksportuj funkcję middleware (lub eksport domyślny) oraz opcjonalny config.
import { NextResponse, type NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest) { if (!request.cookies.has("session")) { return NextResponse.redirect(new URL("/login", request.url)); } return NextResponse.next();}export const config = { matcher: ["/dashboard/:path*", "/api/:path*"],};Middleware uruchamia się przed handlerami tras, plikami statycznymi i stronami konsoli, w text dev, text start i każdym serwerowym adapterze wdrożeniowym. Korzysta z interfejsów Web Request i Response, więc ten sam plik działa w środowiskach Node, Bun, Vercel Edge i Cloudflare Workers.
Dopasowywanie ścieżek
Bez config.matcher middleware uruchamia się dla każdego żądania. Wzorce dopasowania używają tej samej składni co Next.js:
| Wzorzec | Dopasowania |
|---|---|
/about | /about i /about/ |
/blog/:slug | /blog/hello |
/docs/:path* | /docs, /docs/a/b |
/a/:rest+ | /a/b, ale nie /a |
/user/:id(\\d+) | /user/42, ale nie /user/ada |
/((?!api/).*) | Wszystko oprócz /api/* |
Dodaj warunki za pomocą has i missing (nagłówek, plik cookie, zapytanie lub host):
export const config = { matcher: [{ source: "/beta/:path*", has: [{ type: "cookie", key: "beta", value: "1" }] }],};Wzorce dopasowania dotyczą ścieżki bez basePath. W text dev żądania własnych modułów Vite (/@vite/*, pliki źródłowe) nigdy nie uruchamiają middleware.
Każdy host ustala jedną kanoniczną ścieżkę przed uruchomieniem middleware i używa jej do dopasowywania, w request.nextUrl oraz do wyszukiwania trasy, handlera i pliku. Sekwencje procentowe są dekodowane, a ścieżka jest ponownie kodowana tak, jak zapisuje ją new URL(); segmenty . i .. są rozwiązywane, a powtórzone i końcowe ukośniki usuwane. /ad%6din//users/ dociera do middleware jako /admin/users, natomiast /café pozostaje /caf%C3%A9. Wzorzec z końcowym ukośnikiem, taki jak /admin/, dopasowuje /admin. Żądania, których ścieżka zawiera zakodowany ukośnik lub ukośnik odwrotny (%2F, %5C), znak sterujący lub nieprawidłowe kodowanie procentowe, otrzymują 400. Trasy rozróżniają wielkość liter: /Admin nie pasuje do /admin, a pliki statyczne są udostępniane wyłącznie pod swoją dokładną nazwą.
Dopasuj adresy URL, pod którymi plik jest udostępniany, nie tylko jego nazwę. Cloudflare Pages udostępnia report.html także pod /report, więc wzorzec dla /report.html nie chroni /report. Wymień oba adresy, matcher: ["/report", "/report.html"], lub przechowuj chronione pliki w katalogu i dopasuj go, na przykład za pomocą /reports/:path* dla plików w public/reports/.
NextResponse
| API | Efekt |
|---|---|
NextResponse.next() | Przejście do trasy |
NextResponse.redirect(url, status?) | Domyślnie 307; url musi być bezwzględny |
NextResponse.rewrite(url) | Udostępnienie innej ścieżki; zewnętrzne źródła są obsługiwane przez proxy |
NextResponse.json(body, init?) | Odpowiedź w formacie JSON |
new Response(...) | Odpowiedź z dowolną zawartością |
Nie zwracaj niczego, aby kontynuować bez zmian. next() i rewrite() przyjmują { status }, aby zmienić status końcowej odpowiedzi. Tylko odpowiedzi utworzone przez NextResponse.next() / NextResponse.rewrite() sterują routingiem: każda inna Response, także zwrócona przez fetch(), trafia do klienta bez zmian po usunięciu nagłówków x-middleware-*.
request.nextUrl pomija basePath w pathname, natomiast jego href i toString() go uwzględniają. Aby pozostać w obrębie aplikacji, sklonuj go: const url = request.nextUrl.clone(); url.pathname = "/login"; return NextResponse.redirect(url);.
export function middleware(request: NextRequest) { if (request.nextUrl.pathname === "/status") { return NextResponse.rewrite(new URL("/api/health", request.url)); }}Nagłówki
Przekaż request.headers, aby zmienić to, co otrzymują kolejne handlery, oraz headers, aby dodać nagłówki odpowiedzi:
export function middleware(request: NextRequest) { const headers = new Headers(request.headers); headers.set("x-user", "ada"); const response = NextResponse.next({ request: { headers } }); response.headers.set("x-frame", "▼"); return response;}Nagłówki odpowiedzi z middleware są dodawane do końcowej odpowiedzi, chyba że handler trasy ustawi ten sam nagłówek.
Pliki cookie
request.cookies odczytuje nagłówek Cookie; response.cookies zapisuje Set-Cookie.
export function middleware(request: NextRequest) { const theme = request.cookies.get("theme")?.value ?? "geistDark"; const response = NextResponse.next(); response.cookies.set("theme", theme, { httpOnly: true, sameSite: "lax", maxAge: 60 * 60 * 24 }); return response;}Pliki cookie ustawione w odpowiedzi next() lub rewrite() są też widoczne dla handlera trasy w tym samym żądaniu.
Praca w tle
Drugi argument udostępnia waitUntil(), które podtrzymuje działanie środowisk serverless po wysłaniu odpowiedzi:
import type { NextFetchEvent, NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest, event: NextFetchEvent) { event.waitUntil(fetch("https://analytics.example.com", { method: "POST", body: request.url }));}Uwagi
- Strony renderowane po stronie klienta renderują się w przeglądarce, więc przepisanie adresu na inną stronę zmienia udostępniany plik, a nie trasę renderowaną przez aplikację konsolową. Przepisania do handlerów tras, plików statycznych i zewnętrznych źródeł działają zgodnie z oczekiwaniami.
- Middleware może odczytać treść żądania; handlery tras nadal ją otrzymują. Treść żądań wykluczonych przez wzorzec dopasowania nigdy nie jest odczytywana ani zmieniana.
request.urlzawsze ma źródło serwera: cele żądań takie jak//other.host/xsą normalizowane do pojedynczego początkowego ukośnika.- Adapter statyczny nie może uruchamiać middleware.