استكشف تقنيات تعديل الطلبات المتقدمة باستخدام Next.js middleware. تعلم التعامل مع التوجيه المعقد والمصادقة واختبارات A/B واستراتيجيات التوطين لتطبيقات ويب قوية.
حالات Next.js Middleware الطرفية: إتقان أنماط تعديل الطلبات
يوفر Next.js middleware آلية قوية لاعتراض وتعديل الطلبات قبل أن تصل إلى مسارات تطبيقك. تفتح هذه الإمكانية مجموعة واسعة من الاحتمالات، بدءًا من عمليات التحقق البسيطة من المصادقة إلى سيناريوهات اختبار A/B المعقدة واستراتيجيات التدويل. ومع ذلك، يتطلب الاستفادة الفعالة من middleware فهمًا عميقًا لحالاته الطرفية والمزالق المحتملة. يستكشف هذا الدليل الشامل أنماط تعديل الطلبات المتقدمة، ويقدم أمثلة عملية ورؤى قابلة للتنفيذ لمساعدتك في بناء تطبيقات Next.js قوية وعالية الأداء.
فهم أساسيات Next.js Middleware
قبل الغوص في الأنماط المتقدمة، دعنا نلخص أساسيات Next.js middleware. يتم تنفيذ دوال middleware قبل اكتمال الطلب، مما يسمح لك بما يلي:
- إعادة كتابة عناوين URL: إعادة توجيه المستخدمين إلى صفحات مختلفة بناءً على معايير محددة.
- إعادة توجيه المستخدمين: إرسال المستخدمين إلى عناوين URL مختلفة تمامًا، غالبًا لأغراض المصادقة أو التفويض.
- تعديل الترويسات (Headers): إضافة أو إزالة أو تحديث ترويسات HTTP.
- الاستجابة مباشرة: إرجاع استجابة مباشرة من middleware، متجاوزًا مسارات Next.js.
توجد دوال 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) {
// 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: ['/'],
}
في هذا المثال، عندما يزور مستخدم المسار الجذر (/
) لأول مرة، يقوم 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
// 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).*)'
],
}
يستخرج هذا middleware ترويسة Accept-Language
من الطلب ويحدد اللغة المفضلة للمستخدم. إذا كان عنوان URL لا يحتوي بالفعل على بادئة لغة (مثل /en/about
)، يقوم middleware بإعادة توجيه المستخدم إلى عنوان URL المترجم المناسب (مثل /fr/about
للغة الفرنسية). تأكد من أن لديك بنية مجلدات مناسبة في دليل /pages
أو /app
للغات المختلفة. على سبيل المثال، ستحتاج إلى ملف /pages/en/about.js
وملف /pages/fr/about.js
.
اعتبارات عالمية: تأكد من أن تطبيق i18n الخاص بك يتعامل مع اللغات التي تُكتب من اليمين إلى اليسار (مثل العربية والعبرية) بشكل صحيح. أيضًا، فكر في استخدام شبكة توصيل المحتوى (CDN) لخدمة الأصول المترجمة من خوادم أقرب إلى المستخدمين، مما يحسن الأداء.
3. أعلام الميزات (Feature Flags)
تسمح لك أعلام الميزات بتمكين أو تعطيل الميزات في تطبيقك دون الحاجة إلى نشر كود جديد. هذا مفيد بشكل خاص لطرح الميزات الجديدة تدريجيًا أو لاختبار الميزات في بيئة الإنتاج. يمكن استخدام 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()
}
// Optionally redirect to a "feature unavailable" page
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
. تذكر تهيئة متغيرات البيئة بشكل مناسب للبيئات المختلفة (التطوير، الاختبار، الإنتاج).
اعتبارات عالمية: عند استخدام أعلام الميزات، ضع في اعتبارك الآثار القانونية لتمكين الميزات التي قد لا تتوافق مع اللوائح في جميع المناطق. على سبيل المثال، قد تحتاج الميزات المتعلقة بخصوصية البيانات إلى التعطيل في بلدان معينة.
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`.
اعتبارات عالمية: تأكد من أن منطق كشف الأجهزة لديك يأخذ في الاعتبار الاختلافات الإقليمية في استخدام الأجهزة. على سبيل المثال، لا تزال الهواتف العادية (feature phones) منتشرة في بعض البلدان النامية. فكر في استخدام مزيج من كشف User-Agent وتقنيات التصميم المتجاوب للحصول على حل أكثر قوة.
5. إثراء ترويسات الطلب
يمكن لـ middleware إضافة معلومات إلى ترويسات الطلب قبل معالجتها بواسطة مسارات تطبيقك. هذا مفيد لإضافة بيانات وصفية مخصصة، مثل أدوار المستخدم، أو حالة المصادقة، أو معرفات الطلب، والتي يمكن استخدامها بواسطة منطق تطبيقك.
مثال: إضافة معرف طلب
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
}
يولد هذا middleware معرف طلب فريدًا باستخدام مكتبة uuid
ويضيفه إلى ترويسة x-request-id
. يمكن بعد ذلك استخدام هذا المعرف لأغراض التسجيل والتتبع وتصحيح الأخطاء. تذكر تثبيت حزمة `uuid`: `npm install uuid`.
اعتبارات عالمية: عند إضافة ترويسات مخصصة، كن على دراية بحدود حجم الترويسة. يمكن أن يؤدي تجاوز هذه الحدود إلى أخطاء غير متوقعة. تأكد أيضًا من حماية أي معلومات حساسة تضاف إلى الترويسات بشكل صحيح، خاصة إذا كان تطبيقك خلف وكيل عكسي (reverse proxy) أو شبكة توصيل محتوى (CDN).
6. تحسينات أمنية: تحديد معدل الطلبات (Rate Limiting)
يمكن لـ middleware أن يعمل كخط دفاع أول ضد الهجمات الخبيثة عن طريق تطبيق تحديد معدل الطلبات. هذا يمنع إساءة الاستخدام عن طريق تحديد عدد الطلبات التي يمكن للعميل إجراؤها خلال نافذة زمنية محددة.
مثال: تحديد معدل الطلبات الأساسي باستخدام مخزن بسيط
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
}
يحافظ هذا المثال على مخزن بسيط في الذاكرة (requestCounts
) لتتبع عدد الطلبات من كل عنوان IP. إذا تجاوز العميل MAX_REQUESTS_PER_WINDOW
خلال WINDOW_SIZE_MS
، يقوم middleware بإرجاع خطأ 429 Too Many Requests
. مهم: هذا مثال مبسط وغير مناسب لبيئات الإنتاج لأنه لا يتوسع وهو عرضة لهجمات حجب الخدمة. للاستخدام في الإنتاج، فكر في استخدام حل أكثر قوة لتحديد معدل الطلبات مثل Redis أو خدمة مخصصة لتحديد المعدل.
اعتبارات عالمية: يجب تكييف استراتيجيات تحديد معدل الطلبات مع الخصائص المحددة لتطبيقك والتوزيع الجغرافي للمستخدمين. فكر في استخدام حدود معدلات مختلفة لمناطق مختلفة أو شرائح مستخدمين مختلفة.
الحالات الطرفية والمزالق المحتملة
على الرغم من أن middleware أداة قوية، فمن الضروري أن تكون على دراية بحدودها ومزالقها المحتملة:
- التأثير على الأداء: يضيف middleware عبئًا إضافيًا على كل طلب. تجنب إجراء عمليات مكلفة حسابيًا في middleware، حيث يمكن أن يؤثر ذلك بشكل كبير على الأداء. قم بتحليل أداء middleware الخاص بك لتحديد وتحسين أي اختناقات في الأداء.
- التعقيد: يمكن أن يؤدي الإفراط في استخدام middleware إلى جعل تطبيقك أصعب في الفهم والصيانة. استخدم middleware بحكمة وتأكد من أن كل دالة middleware لها غرض واضح ومحدد جيدًا.
- الاختبار: قد يكون اختبار middleware صعبًا، حيث يتطلب محاكاة طلبات HTTP وفحص الاستجابات الناتجة. استخدم أدوات مثل Jest و Supertest لكتابة اختبارات وحدة وتكامل شاملة لدوال middleware الخاصة بك.
- إدارة ملفات تعريف الارتباط: كن حذرًا عند تعيين ملفات تعريف الارتباط في middleware، حيث يمكن أن يؤثر ذلك على سلوك التخزين المؤقت (caching). تأكد من أنك تفهم الآثار المترتبة على التخزين المؤقت المستند إلى ملفات تعريف الارتباط وقم بتهيئة ترويسات ذاكرة التخزين المؤقت الخاصة بك وفقًا لذلك.
- متغيرات البيئة: تأكد من أن جميع متغيرات البيئة المستخدمة في middleware الخاص بك مهيأة بشكل صحيح للبيئات المختلفة (التطوير، الاختبار، الإنتاج). استخدم أداة مثل Dotenv لإدارة متغيرات البيئة الخاصة بك.
- حدود دوال الحافة (Edge Function): تذكر أن middleware يعمل كدوال حافة، والتي لها قيود على وقت التنفيذ واستخدام الذاكرة وحجم الكود المجمع. حافظ على دوال middleware الخاصة بك خفيفة وفعالة.
أفضل الممارسات لاستخدام Next.js Middleware
لتحقيق أقصى استفادة من Next.js middleware وتجنب المشاكل المحتملة، اتبع أفضل الممارسات التالية:
- اجعلها بسيطة: يجب أن يكون لكل دالة middleware مسؤولية واحدة ومحددة جيدًا. تجنب إنشاء دوال middleware معقدة بشكل مفرط تؤدي مهام متعددة.
- التحسين من أجل الأداء: قلل من كمية المعالجة التي تتم في middleware لتجنب اختناقات الأداء. استخدم استراتيجيات التخزين المؤقت لتقليل الحاجة إلى الحسابات المتكررة.
- الاختبار الشامل: اكتب اختبارات وحدة وتكامل شاملة لدوال middleware الخاصة بك للتأكد من أنها تعمل كما هو متوقع.
- توثيق الكود الخاص بك: قم بتوثيق غرض ووظائف كل دالة middleware بوضوح لتحسين قابلية الصيانة.
- مراقبة تطبيقك: استخدم أدوات المراقبة لتتبع أداء ومعدلات الخطأ في دوال middleware الخاصة بك.
- فهم ترتيب التنفيذ: كن على دراية بالترتيب الذي يتم به تنفيذ دوال middleware، حيث يمكن أن يؤثر ذلك على سلوكها.
- استخدام متغيرات البيئة بحكمة: استخدم متغيرات البيئة لتهيئة دوال middleware الخاصة بك لبيئات مختلفة.
الخاتمة
يقدم Next.js middleware طريقة قوية لتعديل الطلبات وتخصيص سلوك تطبيقك عند الحافة. من خلال فهم أنماط تعديل الطلبات المتقدمة التي تمت مناقشتها في هذا الدليل، يمكنك بناء تطبيقات Next.js قوية وعالية الأداء ومدركة عالميًا. تذكر أن تدرس بعناية الحالات الطرفية والمزالق المحتملة، واتبع أفضل الممارسات الموضحة أعلاه لضمان أن تكون دوال middleware الخاصة بك موثوقة وقابلة للصيانة. احتضن قوة middleware لإنشاء تجارب مستخدم استثنائية وإطلاق العنان لإمكانيات جديدة لتطبيقات الويب الخاصة بك.