العربية

استكشف تقنيات تعديل الطلبات المتقدمة باستخدام Next.js middleware. تعلم التعامل مع التوجيه المعقد والمصادقة واختبارات A/B واستراتيجيات التوطين لتطبيقات ويب قوية.

حالات Next.js Middleware الطرفية: إتقان أنماط تعديل الطلبات

يوفر Next.js middleware آلية قوية لاعتراض وتعديل الطلبات قبل أن تصل إلى مسارات تطبيقك. تفتح هذه الإمكانية مجموعة واسعة من الاحتمالات، بدءًا من عمليات التحقق البسيطة من المصادقة إلى سيناريوهات اختبار A/B المعقدة واستراتيجيات التدويل. ومع ذلك، يتطلب الاستفادة الفعالة من middleware فهمًا عميقًا لحالاته الطرفية والمزالق المحتملة. يستكشف هذا الدليل الشامل أنماط تعديل الطلبات المتقدمة، ويقدم أمثلة عملية ورؤى قابلة للتنفيذ لمساعدتك في بناء تطبيقات Next.js قوية وعالية الأداء.

فهم أساسيات Next.js Middleware

قبل الغوص في الأنماط المتقدمة، دعنا نلخص أساسيات Next.js middleware. يتم تنفيذ دوال middleware قبل اكتمال الطلب، مما يسمح لك بما يلي:

توجد دوال 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 أداة قوية، فمن الضروري أن تكون على دراية بحدودها ومزالقها المحتملة:

أفضل الممارسات لاستخدام Next.js Middleware

لتحقيق أقصى استفادة من Next.js middleware وتجنب المشاكل المحتملة، اتبع أفضل الممارسات التالية:

الخاتمة

يقدم Next.js middleware طريقة قوية لتعديل الطلبات وتخصيص سلوك تطبيقك عند الحافة. من خلال فهم أنماط تعديل الطلبات المتقدمة التي تمت مناقشتها في هذا الدليل، يمكنك بناء تطبيقات Next.js قوية وعالية الأداء ومدركة عالميًا. تذكر أن تدرس بعناية الحالات الطرفية والمزالق المحتملة، واتبع أفضل الممارسات الموضحة أعلاه لضمان أن تكون دوال middleware الخاصة بك موثوقة وقابلة للصيانة. احتضن قوة middleware لإنشاء تجارب مستخدم استثنائية وإطلاق العنان لإمكانيات جديدة لتطبيقات الويب الخاصة بك.