404 Not Found

404 Not Found


nginx

الوسيط (Middleware)

اكتشف Bob أن Alice يمكنها الوصول مباشرة إلى لوحة الإدارة /admin دون أي فحص للصلاحيات. يحتاج Charlie إلى حارس توجيه—يقوم تلقائيًا بإعادة توجيه المستخدمين غير المصادق عليهم الذين يحاولون الوصول إلى صفحة الإدارة إلى صفحة تسجيل الدخول، ويمنع المستخدمين العاديين من الوصول إلى مسارات الإدارة. نظام الوسيط في Nuxt 3 هو الحل.

1. ما ستتعلمه


2. قصة حقيقية لمسؤول

(1) نقطة الألم: ثغرة حرجة بسبب غياب التحكم في الصلاحيات

كتبت Alice مسار /admin في المتصفح ووصلت إلى لوحة إدارة MegaShop، حيث يمكنها عرض جميع الطلبات وبيانات المستخدمين. وجد Bob بصفته مسؤولًا أن الواجهة التي يراها مطابقة تمامًا لواجهة المستخدم العادي، دون أي تمييز. يحتاج Charlie بشكل عاجل إلى تحكم في الوصول على مستوى التوجيه.

(2) حل وسيط التوجيه

في Nuxt 3، يعمل الوسيط قبل انتقال التوجيه ويمكنه إعادة التوجيه أو منع الوصول:

TYPESCRIPT
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  if (!isAuthenticated()) return navigateTo('/login')
})

(3) الفوائد: تحكم في الوصول على مستوى التوجيه

لم تعد Alice قادرة على الوصول مباشرة إلى /admin؛ إذا لم تكن مسجلة الدخول، يتم إعادة توجيهها تلقائيًا إلى صفحة تسجيل الدخول. صلاحيات مسؤول Bob تُفرض على مستوى التوجيه.


3. أنماط الوسيط الثلاثة

(1) ترتيب تنفيذ الوسيط

100%
flowchart LR
    A[Route Navigation] --> B[Global Middleware 1]
    B --> C[Global Middleware 2]
    C --> D[Named/Inline Middleware]
    D --> E[Page Render]
    D -->|abort| F[Redirect / Abort]
    B -->|abort| F
    C -->|abort| F

(2) مقارنة الأنماط الثلاثة

البُعد الوسيط العام الوسيط المُسمّى الوسيط المضمن
اسم الملف *.global.ts *.ts (بدون "global") بدون ملف
النطاق جميع المسارات صفحات محددة الصفحة الحالية
طريقة التعريف مجلد middleware/ مجلد middleware/ داخل definePageMeta
إمكانية إعادة الاستخدام ✅ إعادة استخدام تلقائية ✅ مرجع متعدد الصفحات ❌ الصفحة الحالية فقط
حالات الاستخدام مصادقة/تسجيل عام حارس خاص بالصفحة فحص بسيط لمرة واحدة

4. الوسيط العام

(1) ▶ مثال: وسيط المصادقة العام

TYPESCRIPT
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  # تخطي المصادقة للصفحات العامة
  const publicPages = ['/', '/products', '/about', '/login', '/register']
  if (publicPages.includes(to.path)) return

  const token = useCookie('auth-token')
  if (!token.value) {
    return navigateTo(`/login?redirect=${to.path}`, { redirectCode: 302 })
  }
})

الناتج:

TEXT
// Execution Successful

(2) ▶ مثال: وسيط التسجيل العام

TYPESCRIPT
// middleware/log.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  console.log(`Navigation: ${from.path} → ${to.path}`)
  # عدم إرجاع = السماح بالتنقل
})

الناتج:

TEXT
// Execution Successful
⚠️ ملاحظة: يتم تنفيذ الوسيط العام بترتيب أبجدي لاسم الملف. 01-auth.global.ts يعمل قبل 02-log.global.ts. نوصي باستخدام بادئة رقمية للتحكم في الترتيب.


5. الوسيط المُسمّى

(1) ▶ مثال: وسيط دور المسؤول

TYPESCRIPT
// middleware/admin.ts
export default defineNuxtRouteMiddleware((to) => {
  const user = useState('user')

  if (!user.value) {
    return navigateTo(`/login?redirect=${to.path}`)
  }

  if (user.value.role !== 'admin') {
    # مستخدم عادي يحاول الوصول إلى صفحة الإدارة
    throw createError({
      statusCode: 403,
      message: 'Access denied. Admin role required.'
    })
  }
})

الناتج:

TEXT
// Execution Successful

(2) ▶ مثال: الإشارة إلى وسيط مُسمّى في صفحة

VUE
<!-- pages/admin/index.vue -->
<template>
  <div>
    <h1>Admin Dashboard</h1>
    <p>Welcome, Bob (Admin)</p>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: 'admin', # الإشارة إلى وسيط مُسمّى
  layout: 'sidebar'
})
</script>

الناتج:

TEXT
// Execution Successful

(3) ▶ مثال: الجمع بين عدة وسطاء

VUE
<!-- pages/admin/settings.vue -->
<template>
  <div>
    <h1>Admin Settings</h1>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: ['auth', 'admin'], # التنفيذ بالترتيب: auth ← admin
  layout: 'sidebar'
})
</script>

الناتج:

TEXT
// Execution Successful

6. الوسيط المضمن

(1) ▶ مثال: تعريف وسيط داخل صفحة

VUE
<!-- pages/checkout.vue -->
<template>
  <div>
    <h1>Checkout</h1>
    <p>Cart total: ${{ total }} USD</p>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: defineNuxtRouteMiddleware((to) => {
    # مضمن: التحقق من أن سلة التسوق تحتوي على عناصر
    const cart = useState<any[]>('cart')
    if (cart.value.length === 0) {
      return navigateTo('/cart')
    }
  })
})

const cart = useState<any[]>('cart')
const total = computed(() => cart.value.reduce((s, i) => s + i.price, 0))
</script>

الناتج:

TEXT
// Execution Successful

(1) الاختيار بين الوسيط المضمن والمُسمّى

السيناريو الاختيار السبب
مصادقة على مستوى الموقع عام فحص كل مسار
مسارات الإدارة مُسمّى مشترك بين عدة صفحات إدارة
التحقق من أن سلة التسوق غير فارغة مضمن يُستخدم فقط في صفحة الدفع
منطقة VIP مُسمّى قد يُستخدم في عدة صفحات
تقسيم حركة A/B Testing مضمن منطق لمرة واحدة

7. الوسيط و SSR

(1) آلية التنفيذ المزدوج في SSR

100%
flowchart TB
    A[User navigates to /admin] --> B[Server: middleware executes]
    B -->|redirect| C[Server sends redirect response]
    B -->|allow| D[Server renders page]
    D --> E[Client: middleware executes again]
    E -->|redirect| F[Client-side redirect]
    E -->|allow| G[Client hydrates page]

(1) ▶ مثال: وسيط من جانب العميل فقط

TYPESCRIPT
// middleware/client-only.ts
export default defineNuxtRouteMiddleware((to) => {
  # تخطي على الخادم - الفحص على العميل فقط
  if (import.meta.server) return

  const localStorage = window.localStorage
  const preferences = JSON.parse(localStorage.getItem('user-preferences') || '{}')

  if (preferences.theme === 'dark') {
    # منطق من جانب العميل فقط باستخدام واجهات برمجة المتصفح
    document.documentElement.classList.add('dark')
  }
})

الناتج:

TEXT
// Execution Successful

(2) اعتبارات وسيط SSR

المشكلة السبب الحل
الوسيط يعمل مرتين SSR والعميل كل منهما يعمل مرة سلوك متوقع؛ ضمان التساوي
استخدام window/localStorage SSR (لا بيئة متصفح) فحص if (import.meta.client)
ملفات تعريف الارتباط متاحة في SSR الطلبات تحمل ملفات تعريف الارتباط قراءتها باستخدام useCookie
navigateTo في SSR يُرجع إعادة توجيه 302 سلوك متوقع
useState في الوسيط ✅ متاح مناسب لفحص حالة تسجيل الدخول

8. مثال شامل: نظام حماية صلاحيات MegaShop

TYPESCRIPT
// middleware/01-auth.global.ts - فحص المصادقة العام
export default defineNuxtRouteMiddleware((to) => {
  const publicPaths = ['/', '/products', '/products/**', '/about', '/login', '/register']
  const isPublic = publicPaths.some(path => {
    if (path.includes('')) return to.path.startsWith(path.replace('/', ''))
    return path === to.path
  })

  if (isPublic) return

  const token = useCookie('auth-token')
  if (!token.value) {
    return navigateTo(`/login?redirect=${encodeURIComponent(to.path)}`)
  }
})
TYPESCRIPT
// middleware/admin.ts - فحص دور المسؤول
export default defineNuxtRouteMiddleware((to) => {
  const user = useCookie('user-data')

  if (!user.value) {
    return navigateTo('/login')
  }

  if ((user.value as any).role !== 'admin') {
    throw createError({ statusCode: 403, message: 'Admin access required' })
  }
})
VUE
<!-- pages/admin/products/index.vue -->
<template>
  <div>
    <h1>Product Management</h1>
    <p>Manage 1 million products</p>
    <!-- واجهة إدارة منتجات المسؤول -->
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: ['admin'],
  layout: 'sidebar'
})
</script>

❓ أسئلة شائعة

س كيف يتم التحكم في ترتيب تنفيذ الوسيط؟
ج يتم ترتيب الوسيط العام أبجديًا حسب اسم الملف؛ نوصي باستخدام بادئة رقمية (01-xxx.global.ts). الوسيط المُسمّى يُنفَّذ بالترتيب المحدد في المصفوفة داخل definePageMeta.
س هل يمكن تنفيذ عمليات غير متزامنة في الوسيط؟
ج defineNuxtRouteMiddleware بحد ذاته لا يدعم async. ومع ذلك، يمكنك استدعاء واجهات برمجة متزامنة (مثل useCookie أو useState) داخل الوسيط. إذا كنت بحاجة إلى إجراء تحقق غير متزامن، يُنصح بمعالجته في إضافة أو في طريقة setup للصفحة.
س لماذا يتم تنفيذ الوسيط مرة أثناء SSR ومرة على جانب العميل؟
ج أثناء SSR، يفحص الوسيط صلاحيات التوجيه لتحديد ما إذا كان سيتم عرض الصفحة؛ ويُنفَّذ مرة أخرى أثناء ترطيب جانب العميل لضمان الاتساق. يجب أن يكون منطق التنفيذين متساويًا.
س هل يؤثر الوسيط العام على الأداء؟
ج يتم تنفيذ جميع الوسطاء العامين في كل مرة يتم فيها التنقل في مسار. أبقِ منطق الوسيط خفيفًا، وتجنب إجراء حسابات ثقيلة أو استدعاءات API داخل الوسيط العام.
س ماذا يحدث إذا تم إلقاء createError في الوسيط؟
ج أثناء SSR، يتم إرجاع صفحة خطأ HTML مع رمز الحالة المقابل (مثل صفحة 403). على جانب العميل، يتم عرض صفحة خطأ Nuxt. يمكنك تخصيص error.vue.
س كيف تمرر البيانات بين الوسطاء؟
ج استخدم useState أو خاصية payload في NuxtApp. const nuxtApp = useNuxtApp()nuxtApp.payload.xxx = data. يمكن للوسيط أو الصفحة التالية الوصول إليها.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): أنشئ وسيطًا باسم guest.ts يسمح فقط للمستخدمين غير المصادق عليهم بالوصول إلى الصفحة (يتم إعادة توجيه المستخدمين المسجلين إلى الصفحة الرئيسية)، وطبّقه على صفحة /login.
  2. تمرين متقدم (الصعوبة: ⭐⭐): نفّذ نظام ترخيص كامل: فحص مصادقة عام لتسجيل الدخول + فحص دور المستخدم "admin"؛ مسار /admin/** محمي.
  3. تحدٍ (الصعوبة: ⭐⭐⭐): نفّذ تمرير البيانات بين الوسطاء—استرجع معلومات المستخدم في الوسيط auth وخزّنها في حمولة NuxtApp، ثم اقرأها مباشرة في الوسيط admin لتجنب الطلبات المكررة

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%