Next.js
مسیریاب app
برنامه تان را بسیازید
مسیریابی Routing
میان افزار

میان افزار

میان افزار به شما امکان می‌دهد قبل از تکمیل درخواست، کد را اجرا کنید. سپس، بر اساس درخواست ورودی، می‌توانید با بازنویسی، تغییر مسیر، تغییر سرصفحه‌های درخواست یا پاسخ، یا پاسخ مستقیم، پاسخ را تغییر دهید.

میان افزار قبل از محتوای کش شده و مطابقت با مسیرها اجرا می‌شود. برای جزئیات بیشتر Matching Paths را ببینید.

موارد استفاده

ادغام میان افزار در برنامه شما می‌تواند منجر به بهبود قابل توجهی در عملکرد، امنیت و تجربه کاربری شود. برخی از سناریوهای رایج که میان افزار در آنها به خصوص مؤثر است عبارتند از:

  • احراز هویت و مجوز: قبل از اعطای دسترسی به صفحات یا مسیرهای API خاص، از هویت کاربر اطمینان حاصل کنید و کوکی‌های جلسه را بررسی کنید.
  • تغییر مسیر سمت سرور: کاربران را بر اساس شرایط خاص (به عنوان مثال، محل locale، user role نقش کاربر) در سطح سرور هدایت کنید.
  • بازنویسی مسیر: با بازنویسی مسیرها به صورت داینامیک به مسیرهای API یا صفحات بر اساس ویژگی‌های درخواست، از تست A/B، راه‌اندازی ویژگی‌ها یا مسیرهای قدیمی پشتیبانی کنید.
  • تشخیص ربات: با شناسایی و مسدود کردن ترافیک ربات، از منابع خود محافظت کنید.
  • ورود به سیستم و تجزیه و تحلیل: قبل از پردازش توسط صفحه یا API، داده‌های درخواست را برای بینش ثبت و تجزیه و تحلیل کنید.
  • پرچم گذاری ویژگی: برای راه‌اندازی یا تست ویژگی‌های بدون دردسر، ویژگی‌ها را به صورت داینامیک فعال یا غیرفعال کنید.

تشخیص موقعیت هایی که ممکن است میان افزار بهترین روش نباشد به همان اندازه مهم است. در اینجا چند سناریو که باید به آنها توجه کنید آورده شده است:

  • بازیابی و دستکاری داده‌های پیچیده: میان افزار برای بازیابی یا دستکاری مستقیم داده طراحی نشده است، این کار باید به جای آن در هندلر مسیر ها یا ابزارهای سمت سرور انجام شود.
  • کارهای محاسباتی سنگین: میان افزار باید سبک وزن باشد و به سرعت پاسخ دهد، در غیر این صورت می‌تواند باعث تأخیر در بارگذاری صفحه شود. وظایف محاسباتی سنگین یا فرآیندهای طولانی مدت باید در هندلر مسیر های اختصاصی انجام شود.
  • مدیریت گسترده جلسه: در حالی که میان افزار می‌تواند وظایف اولیه جلسه را مدیریت کند، مدیریت گسترده جلسه باید توسط سرویس‌های احراز هویت اختصاصی یا درون هندلر مسیر ها انجام شود.
  • عملیات مستقیم پایگاه داده: انجام عملیات مستقیم پایگاه داده در میان افزار توصیه نمی شود. تعامل با پایگاه داده باید در هندلر مسیر ها یا ابزارهای سمت سرور انجام شود.

قرارداد

از فایل middleware.ts (یا .js) در ریشه پروژه خود برای تعریف میان افزار استفاده کنید. برای مثال، در همان سطح pages یا app، یا در صورت وجود داخل src.

نکته: در حالی که تنها یک فایل middleware.ts به ازای هر پروژه پشتیبانی می‌شود، همچنان می‌توانید منطق میان افزار خود را به صورت modular سازماندهی کنید. عملکردهای میان افزار را به فایل‌های جداگانه .ts یا .js تقسیم کرده و آنها را به فایل اصلی middleware.ts خود وارد کنید. این امکان مدیریت تمیزتر میان افزار های خاص مسیر را فراهم می کند که در middleware.ts برای کنترل متمرکز جمع آوری شده اند. با اجباری کردن یک فایل میان افزار پیکربندی را ساده می‌کند، از تداخل‌های بالقوه جلوگیری می‌کند و با اجتناب از لایه‌های متعدد میان افزار، عملکرد را بهینه می‌کند.

مثال

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
	return NextResponse.redirect(new URL('/home', request.url))
}
 
// See "Matching Paths" below to learn more
export const config = {
	matcher: '/about/:path*',
}

مطابقت مسیرها

میان افزار برای هر مسیر در پروژه شما فراخوانده می‌شود. با توجه به این، استفاده از تطبیق‌دهنده‌ها (matcher) برای هدف‌گذاری دقیق یا حذف مسیرهای خاص بسیار مهم است. ترتیب اجرای موارد به شرح زیر است:

  1. headers from next.config.js
  2. redirects from next.config.js
  3. Middleware (rewrites, redirects, etc.)
  4. beforeFiles (rewrites) from next.config.js
  5. Filesystem routes (public/, _next/static/, pages/, app/, etc.)
  6. afterFiles (rewrites) from next.config.js
  7. Dynamic Routes (/blog/[slug])
  8. fallback (rewrites) from next.config.js

دو راه برای تعریف اینکه میان افزار روی کدام مسیرها اجرا شود وجود دارد:

  1. پیکربندی تطبیق‌دهنده سفارشی Custom matcher config
  2. عبارات شرطی Conditional statements

تطبیق‌دهنده Matcher

matcher به شما امکان می‌دهد میان افزار را برای اجرا در مسیرهای خاص فیلتر کنید.

middleware.js
export const config = {
	matcher: '/about/:path*',
}

شما می‌توانید با استفاده از سینتکس آرایه، یک مسیر یا چندین مسیر را مطابقت دهید:

middleware.js
export const config = {
	matcher: ['/about/:path*', '/dashboard/:path*'],
}

پیکربندی matcher regex کامل را امکان پذیر می کند، بنابراین مطابقت‌هایی مانند lookaheadهای منفی یا مطابقت کاراکتر پشتیبانی می‌شود. نمونه‌ای از یک negative lookahead برای مطابقت با همه مسیرها به جز مسیرهای خاص را می‌توانید در اینجا ببینید:

middleware.js
export const config = {
	matcher: [
		/*
		 * Match all request paths except for the ones starting with:
		 * - api (API routes)
		 * - _next/static (static files)
		 * - _next/image (image optimization files)
		 * - favicon.ico (favicon file)
		 */
		'/((?!api|_next/static|_next/image|favicon.ico).*)',
	],
}

همچنین می‌توانید با استفاده از آرایه‌های missing یا has یا ترکیبی از هر دو، میان افزار را برای درخواست‌های خاص دور بزنید.

middleware.js
export const config = {
	matcher: [
		/*
		 * Match all request paths except for the ones starting with:
		 * - api (API routes)
		 * - _next/static (static files)
		 * - _next/image (image optimization files)
		 * - favicon.ico (favicon file)
		 */
		{
			source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
			missing: [
				{ type: 'header', key: 'next-router-prefetch' },
				{ type: 'header', key: 'purpose', value: 'prefetch' },
			],
		},
 
		{
			source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
			has: [
				{ type: 'header', key: 'next-router-prefetch' },
				{ type: 'header', key: 'purpose', value: 'prefetch' },
			],
		},
 
		{
			source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
			has: [{ type: 'header', key: 'x-present' }],
			missing: [{ type: 'header', key: 'x-missing', value: 'prefetch' }],
		},
	],
}

خوب است بدانید: مقادیر matcher باید ثابت باشند تا بتوان آنها را در زمان ساخت به صورت استاتیک آنالیز کرد. مقادیر داینامیک مانند متغیرها نادیده گرفته می‌شوند.

پیکربندی تطبیق‌ دهنده‌ها:

  1. باید با / شروع شوند
  2. می‌توانند شامل پارامترهای نامگذاری شده باشند: /about/:path مطابق با /about/a و /about/b اما نه /about/a/c
  3. می‌توانند روی پارامترهای نامگذاری شده تغییردهنده داشته باشند (با : شروع می‌شود): /about/:path* مطابق با /about/a/b/c زیرا * صفر یا بیشتر است. ? صفر یا یک است و + یک یا بیشتر
  4. می‌توانند از عبارت باقاعده محصور در پرانتز استفاده کنند: /about/(.*) همان /about/:path* است

جزئیات بیشتر در مستندات path-to-regexp (opens in a new tab) را بخوانید.

خوب است بدانید: برای سازگاری با نسخه‌های قبلی، Next.js همیشه /public را به عنوان /public/index در نظر می‌گیرد. بنابراین، یک matcher از /public/:path مطابقت خواهد داشت.

عبارت‌های شرطی

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
	if (request.nextUrl.pathname.startsWith('/about')) {
		return NextResponse.rewrite(new URL('/about-2', request.url))
	}
 
	if (request.nextUrl.pathname.startsWith('/dashboard')) {
		return NextResponse.rewrite(new URL('/dashboard/user', request.url))
	}
}

NextResponse

NextResponse API به شما امکان می‌دهد:

  • تغییر مسیر (redirect) درخواست ورودی به یک URL دیگر
  • بازنویسی (rewrite) پاسخ با نمایش یک URL مشخص
  • تنظیم سرصفحه‌های درخواست برای API Routes، getServerSideProps و rewrite مقصدها
  • تنظیم کوکی‌های پاسخ
  • تنظیم سرصفحه‌های headers پاسخ

برای تولید پاسخ از میان افزار می‌توانید:

  1. به مسیری (صفحه یا هندلر مسیر) که یک پاسخ تولید می‌کند بازنویسی (rewrite) کنید
  2. مستقیماً یک NextResponse برگردانید. بخش تولید پاسخ را ببینید.

استفاده از کوکی‌ها (Using Cookies)

کوکی‌ها هدرهای معمولی هستند. در یک درخواست Request، آنها در هدر Cookie ذخیره می‌شوند. در یک پاسخ Response آنها در هدر Set-Cookie قرار دارند. Next.js یک روش راحت برای دسترسی و دستکاری این کوکی‌ها از طریق افزونه کوکی‌ها روی NextRequest و NextResponse ارائه می‌دهد.

  1. برای درخواست‌های ورودی، cookies با روش‌های زیر همراه هستند: get، getAll، set و delete کوکی‌ها. می‌توانید با has وجود کوکی را بررسی کنید یا با clear همه کوکی‌ها را حذف کنید.
  2. برای پاسخ‌های خروجی، cookies دارای روش‌های get، getAll، set و delete هستند.
middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
	// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
	// Getting cookies from the request using the `RequestCookies` API
	let cookie = request.cookies.get('nextjs')
	console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
	const allCookies = request.cookies.getAll()
	console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
 
	request.cookies.has('nextjs') // => true
	request.cookies.delete('nextjs')
	request.cookies.has('nextjs') // => false
 
	// Setting cookies on the response using the `ResponseCookies` API
	const response = NextResponse.next()
	response.cookies.set('vercel', 'fast')
	response.cookies.set({
		name: 'vercel',
		value: 'fast',
		path: '/',
	})
	cookie = response.cookies.get('vercel')
	console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
	// The outgoing response will have a `Set-Cookie:vercel=fast;path=/` header.
 
	return response
}

تنظیم هدرها

می‌توانید با استفاده از NextResponse API هدرهای درخواست و پاسخ را تنظیم کنید (تنظیم هدرهای درخواست از Next.js نسخه ۱۳ به بعد در دسترس است).

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
	// Clone the request headers and set a new header `x-hello-from-middleware1`
	const requestHeaders = new Headers(request.headers)
	requestHeaders.set('x-hello-from-middleware1', 'hello')
 
	// You can also set request headers in NextResponse.rewrite
	const response = NextResponse.next({
		request: {
			// New request headers
			headers: requestHeaders,
		},
	})
 
	// Set a new response header `x-hello-from-middleware2`
	response.headers.set('x-hello-from-middleware2', 'hello')
	return response
}

خوب است بدانید: از تنظیم هدرهای بزرگ خودداری کنید زیرا ممکن است باعث خطای 431 Request Header Fields Too Large (opens in a new tab) بسته به پیکربندی سرور وب پشتیبان شما شود.

CORS (Cross-origin resource sharing)

می‌توانید هدرهای CORS را در میان افزار برای اجازه دادن به درخواست‌های متقابل (cross-origin) تنظیم کنید، از جمله درخواست‌های ساده (opens in a new tab) و preflighted (opens in a new tab).

middleware.ts
import { NextRequest, NextResponse } from 'next/server'
 
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
 
const corsOptions = {
	'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
	'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
 
export function middleware(request: NextRequest) {
	// Check the origin from the request
	const origin = request.headers.get('origin') ?? ''
	const isAllowedOrigin = allowedOrigins.includes(origin)
 
	// Handle preflighted requests
	const isPreflight = request.method === 'OPTIONS'
 
	if (isPreflight) {
		const preflightHeaders = {
			...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
			...corsOptions,
		}
		return NextResponse.json({}, { headers: preflightHeaders })
	}
 
	// Handle simple requests
	const response = NextResponse.next()
 
	if (isAllowedOrigin) {
		response.headers.set('Access-Control-Allow-Origin', origin)
	}
 
	Object.entries(corsOptions).forEach(([key, value]) => {
		response.headers.set(key, value)
	})
 
	return response
}
 
export const config = {
	matcher: '/api/:path*',
}

خوب است بدانید: می‌توانید هدرهای CORS را برای مسیرهای جداگانه در هندلرهای مسیر پیکربندی کنید.

تولید پاسخ Producing a Response

می‌توانید مستقیماً با برگرداندن یک نمونه Response یا NextResponse از میان افزار پاسخ دهید. (این قابلیت از Next.js نسخه ۱۳.۱ (opens in a new tab) به بعد در دسترس است)

middleware.ts
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
 
// Limit the middleware to paths starting with `/api/`
export const config = {
	matcher: '/api/:function*',
}
 
export function middleware(request: NextRequest) {
	// Call our authentication function to check the request
	if (!isAuthenticated(request)) {
		// Respond with JSON indicating an error message
		return Response.json(
			{ success: false, message: 'authentication failed' },
			{ status: 401 }
		)
	}
}

waitUntil و NextFetchEvent

شیء NextFetchEvent شیء FetchEvent (opens in a new tab) بومی را گسترش می‌دهد و شامل متد waitUntil() (opens in a new tab) می‌شود.

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

middleware.ts
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'
 
export function middleware(req: NextRequest, event: NextFetchEvent) {
	event.waitUntil(
		fetch('https://my-analytics-platform.com', {
			method: 'POST',
			body: JSON.stringify({ pathname: req.nextUrl.pathname }),
		})
	)
 
	return NextResponse.next()
}

پرچم‌های پیشرفته میان افزار Flags

در v13.1 Next.js، دو پرچم اضافی برای میان افزار معرفی شد، skipMiddlewareUrlNormalize و skipTrailingSlashRedirect برای رسیدگی به موارد استفاده پیشرفته.

skipTrailingSlashRedirect بازنویسی‌های Next.js را برای اضافه یا حذف اسلش‌های انتهایی غیرفعال می‌کند. این به میان افزار اجازه می‌دهد تا به صورت سفارشی برای حفظ اسلش انتهایی در برخی مسیرها اما نه مسیرهای دیگر عمل کند که می‌تواند مهاجرت‌های افزایشی را آسان‌تر کند.

next.config.js
module.exports = {
	skipTrailingSlashRedirect: true,
}
middleware.js
const legacyPrefixes = ['/docs', '/blog']
 
export default async function middleware(req) {
	const { pathname } = req.nextUrl
 
	if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
		return NextResponse.next()
	}
 
	// apply trailing slash handling
	if (
		!pathname.endsWith('/') &&
		!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
	) {
		return NextResponse.redirect(
			new URL(`${req.nextUrl.pathname}/`, req.nextUrl)
		)
	}
}

skipMiddlewareUrlNormalize به غیرفعال کردن عادی‌سازی URL در Next.js اجازه می‌دهد تا بازدیدهای مستقیم و گذارهای سمت کاربر یکسان شود. در برخی موارد پیشرفته، این گزینه با استفاده از URL اصلی، کنترل کاملی را ارائه می‌دهد.

next.config.js
module.exports = {
	skipMiddlewareUrlNormalize: true,
}
middleware.js
export default async function middleware(req) {
	const { pathname } = req.nextUrl
 
	// GET /_next/data/build-id/hello.json
 
	console.log(pathname)
	// with the flag this now /_next/data/build-id/hello.json
	// without the flag this would be normalized to /hello
}

اجرا Runtime

میان افزار در حال حاضر فقط از Edge runtime پشتیبانی می‌کند. محیط اجرای Node.js قابل استفاده نیست.

تاریخچه نسخه

نسخهتغییرات
v13.1.0پرچم های میان افزار پیشرفته اضافه شد
v13.0.0میان‌افزار می‌تواند سرصفحه‌های درخواست، سرصفحه‌های پاسخ و ارسال پاسخ‌ها را تغییر دهد
v12.2.0میان افزار پایدار است، لطفاً راهنمای ارتقا را ببینید
v12.0.9Enforce absolute URLs in Edge Runtime (PR (opens in a new tab))
v12.0.0میان افزار (بتا) اضافه شد