⁦middleware⁩

⁦middleware.ts⁩: تغییرمسیرها، بازنویسی‌ها، هدرها و کوکی‌ها پیش از هر درخواست، با config.matcher.

⁦middleware⁩ پیش از تکمیل درخواست، کد را اجرا می‌کند. می‌تواند تغییر مسیر دهد، مسیر را بازنویسی کند، هدرها و کوکی‌ها را تنظیم کند یا خودش پاسخی برگرداند.

قرارداد

فایل middleware.ts را در ریشه پروژه (یا src/)، کنار app/ ایجاد کنید. یک تابع middleware (یا یک خروجی پیش‌فرض) و یک config اختیاری صادر کنید.

middleware.tsts
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⁩ پیش از کنترل‌گرهای مسیر، فایل‌های ایستا و صفحه‌های کنسول، در text dev، text start و هر آداپتور استقرار سرور اجرا می‌شود. از Request و Response وب استفاده می‌کند، بنابراین همان فایل روی Node، Bun، ⁦Vercel Edge⁩ و ⁦Cloudflare Workers⁩ اجرا می‌شود.

تطبیق مسیرها

بدون config.matcher، middleware برای هر درخواست اجرا می‌شود. تطبیق‌دهنده‌ها از همان نحو Next.js استفاده می‌کنند:

تطبیق‌دهندهموارد منطبق
/about/about و /about/
/blog/:slug/blog/hello
/docs/:path*/docs، /docs/a/b
/a/:rest+/a/b، به‌جز /a
/user/:id(\\d+)/user/42، به‌جز /user/ada
/((?!api/).*)همه‌چیز به‌جز /api/*

با has و missing شرط اضافه کنید (هدر، کوکی، پرس‌وجو یا میزبان):

middleware.tsts
export const config = {  matcher: [{ source: "/beta/:path*", has: [{ type: "cookie", key: "beta", value: "1" }] }],};

تطبیق‌دهنده‌ها روی نام مسیر بدون basePath اعمال می‌شوند. در text dev، درخواست‌های ماژول خود Vite ‏(/@vite/*، فایل‌های منبع) هرگز middleware را اجرا نمی‌کنند.

هر میزبان پیش از اجرای middleware، یک نام مسیر معیار تعیین می‌کند و از آن برای تطبیق، request.nextUrl و جست‌وجوی مسیر، کنترل‌گر و فایل استفاده می‌کند. کدگذاری‌های درصدی رمزگشایی می‌شوند و مسیر به شیوه‌ای که new URL() آن را می‌نویسد دوباره کدگذاری می‌شود، بخش‌های . و .. حل می‌شوند و اسلش‌های تکراری و انتهایی حذف می‌شوند. مسیر /ad%6din//users/ به‌شکل /admin/users به middleware می‌رسد، درحالی‌که /café به‌شکل /caf%C3%A9 باقی می‌ماند. تطبیق‌دهنده‌ای که با اسلش انتهایی نوشته شده باشد، مانند /admin/، با /admin منطبق می‌شود. درخواست‌هایی که مسیرشان اسلش یا بک‌اسلش کدگذاری‌شده (%2F، %5C)، نویسه کنترلی یا کدگذاری درصدی نامعتبر داشته باشد، پاسخ 400 می‌گیرند. مسیرها به بزرگی و کوچکی حروف حساس‌اند: /Admin با /admin منطبق نمی‌شود و فایل‌های ایستا فقط با نام دقیق خود ارائه می‌شوند.

نشانی‌هایی را که فایل در آن‌ها ارائه می‌شود تطبیق دهید، نه فقط نام فایل را. ⁦Cloudflare Pages⁩ فایل report.html را در /report هم ارائه می‌کند، بنابراین تطبیق‌دهنده‌ای برای /report.html از /report محافظت نمی‌کند. هر دو را در فهرست بیاورید، matcher: ["/report", "/report.html"]، یا فایل‌های محافظت‌شده را در یک دایرکتوری نگه دارید و آن را تطبیق دهید، مانند /reports/:path* برای فایل‌های داخل public/reports/.

NextResponse

⁦API⁩اثر
NextResponse.next()ادامه به مسیر
NextResponse.redirect(url, status?)307 به‌صورت پیش‌فرض؛ url باید مطلق باشد
NextResponse.rewrite(url)ارائه مسیری دیگر؛ مبدأهای خارجی پروکسی می‌شوند
NextResponse.json(body, init?)پاسخ با JSON
new Response(...)پاسخ با هر محتوایی

برای ادامه بدون تغییر، چیزی برنگردانید. توابع next() و rewrite() برای تغییر وضعیت پاسخ نهایی، { status } را می‌پذیرند. فقط پاسخ‌های ساخته‌شده با NextResponse.next() / NextResponse.rewrite() مسیریابی را هدایت می‌کنند: هر Response دیگری، از جمله پاسخی که fetch() برمی‌گرداند، پس از حذف هدرهای x-middleware-*، بدون تغییر به کلاینت فرستاده می‌شود.

در request.nextUrl، مقدار basePath از pathname حذف می‌شود، اما href و toString() آن را شامل می‌شوند. برای ماندن در محدوده برنامه، از آن یک کپی بسازید: const url = request.nextUrl.clone(); url.pathname = "/login"; return NextResponse.redirect(url);.

middleware.tsts
export function middleware(request: NextRequest) {  if (request.nextUrl.pathname === "/status") {    return NextResponse.rewrite(new URL("/api/health", request.url));  }}

هدرها

برای تغییر آنچه کنترل‌گرهای پایین‌دست دریافت می‌کنند، request.headers و برای افزودن هدرهای پاسخ، headers را ارسال کنید:

middleware.tsts
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;}

هدرهای پاسخ middleware به پاسخ نهایی اضافه می‌شوند، مگر اینکه کنترل‌گر مسیر همان هدر را تنظیم کند.

کوکی‌ها

ویژگی request.cookies هدر Cookie را می‌خواند؛ ویژگی response.cookies هدر Set-Cookie را می‌نویسد.

middleware.tsts
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;}

کوکی‌های تنظیم‌شده در پاسخ next() یا rewrite() برای کنترل‌گر مسیر در همان درخواست نیز قابل مشاهده‌اند.

کار پس‌زمینه

آرگومان دوم، waitUntil() را دارد که محیط‌های اجرای بدون سرور را پس از ارسال پاسخ فعال نگه می‌دارد:

middleware.tsts
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 }));}

نکات

  • صفحه‌های رندرشده در کلاینت در مرورگر رندر می‌شوند، بنابراین بازنویسی به صفحه‌ای دیگر، فایل ارائه‌شده را تغییر می‌دهد، نه مسیری را که برنامه کنسول رندر می‌کند. بازنویسی به کنترل‌گرهای مسیر، فایل‌های ایستا و مبدأهای خارجی مطابق انتظار عمل می‌کند.
  • middleware می‌تواند بدنه درخواست را بخواند؛ کنترل‌گرهای مسیر همچنان آن را دریافت می‌کنند. بدنه درخواست‌هایی که تطبیق‌دهنده کنار می‌گذارد هرگز دست‌کاری نمی‌شود.
  • ویژگی request.url همیشه مبدأ سرور را دارد: هدف‌های درخواست مانند //other.host/x به یک اسلش آغازین نرمال‌سازی می‌شوند.
  • آداپتور ایستا نمی‌تواند middleware را اجرا کند.
ویرایش این صفحه در GitHub