الوسيط (Middleware)
اكتشف Bob أن Alice يمكنها الوصول مباشرة إلى لوحة الإدارة /admin دون أي فحص للصلاحيات. يحتاج Charlie إلى حارس توجيه—يقوم تلقائيًا بإعادة توجيه المستخدمين غير المصادق عليهم الذين يحاولون الوصول إلى صفحة الإدارة إلى صفحة تسجيل الدخول، ويمنع المستخدمين العاديين من الوصول إلى مسارات الإدارة. نظام الوسيط في Nuxt 3 هو الحل.
1. ما ستتعلمه
- الوسيط العام: middleware/auth.global.ts - النطاق العام وترتيب التنفيذ
- الوسيط المُسمّى: مزيج من
definePageMetaومصفوفة الوسيط - الوسيط المضمن: وسيط خفيف مُعرّف مباشرة داخل الصفحة
- الوسيط و SSR: آليات التنفيذ المزدوج والوسيط من جانب العميل فقط
- حارس مسار مدير MegaShop + فحص الدور: دليل عملي
2. قصة حقيقية لمسؤول
(1) نقطة الألم: ثغرة حرجة بسبب غياب التحكم في الصلاحيات
كتبت Alice مسار /admin في المتصفح ووصلت إلى لوحة إدارة MegaShop، حيث يمكنها عرض جميع الطلبات وبيانات المستخدمين. وجد Bob بصفته مسؤولًا أن الواجهة التي يراها مطابقة تمامًا لواجهة المستخدم العادي، دون أي تمييز. يحتاج Charlie بشكل عاجل إلى تحكم في الوصول على مستوى التوجيه.
(2) حل وسيط التوجيه
في Nuxt 3، يعمل الوسيط قبل انتقال التوجيه ويمكنه إعادة التوجيه أو منع الوصول:
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
if (!isAuthenticated()) return navigateTo('/login')
})
(3) الفوائد: تحكم في الوصول على مستوى التوجيه
لم تعد Alice قادرة على الوصول مباشرة إلى /admin؛ إذا لم تكن مسجلة الدخول، يتم إعادة توجيهها تلقائيًا إلى صفحة تسجيل الدخول. صلاحيات مسؤول Bob تُفرض على مستوى التوجيه.
3. أنماط الوسيط الثلاثة
(1) ترتيب تنفيذ الوسيط
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) ▶ مثال: وسيط المصادقة العام
// 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 })
}
})
الناتج:
// Execution Successful
(2) ▶ مثال: وسيط التسجيل العام
// middleware/log.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
console.log(`Navigation: ${from.path} → ${to.path}`)
# عدم إرجاع = السماح بالتنقل
})
الناتج:
// Execution Successful
01-auth.global.ts يعمل قبل 02-log.global.ts. نوصي باستخدام بادئة رقمية للتحكم في الترتيب.
5. الوسيط المُسمّى
(1) ▶ مثال: وسيط دور المسؤول
// 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.'
})
}
})
الناتج:
// Execution Successful
(2) ▶ مثال: الإشارة إلى وسيط مُسمّى في صفحة
<!-- 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>
الناتج:
// Execution Successful
(3) ▶ مثال: الجمع بين عدة وسطاء
<!-- 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>
الناتج:
// Execution Successful
6. الوسيط المضمن
(1) ▶ مثال: تعريف وسيط داخل صفحة
<!-- 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>
الناتج:
// Execution Successful
(1) الاختيار بين الوسيط المضمن والمُسمّى
| السيناريو | الاختيار | السبب |
|---|---|---|
| مصادقة على مستوى الموقع | عام | فحص كل مسار |
| مسارات الإدارة | مُسمّى | مشترك بين عدة صفحات إدارة |
| التحقق من أن سلة التسوق غير فارغة | مضمن | يُستخدم فقط في صفحة الدفع |
| منطقة VIP | مُسمّى | قد يُستخدم في عدة صفحات |
| تقسيم حركة A/B Testing | مضمن | منطق لمرة واحدة |
7. الوسيط و SSR
(1) آلية التنفيذ المزدوج في SSR
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) ▶ مثال: وسيط من جانب العميل فقط
// 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')
}
})
الناتج:
// Execution Successful
(2) اعتبارات وسيط SSR
| المشكلة | السبب | الحل |
|---|---|---|
| الوسيط يعمل مرتين | SSR والعميل كل منهما يعمل مرة | سلوك متوقع؛ ضمان التساوي |
| استخدام window/localStorage | SSR (لا بيئة متصفح) | فحص if (import.meta.client) |
| ملفات تعريف الارتباط متاحة في SSR | الطلبات تحمل ملفات تعريف الارتباط | قراءتها باستخدام useCookie |
| navigateTo في SSR | يُرجع إعادة توجيه 302 | سلوك متوقع |
| useState في الوسيط | ✅ متاح | مناسب لفحص حالة تسجيل الدخول |
8. مثال شامل: نظام حماية صلاحيات MegaShop
// 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)}`)
}
})
// 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' })
}
})
<!-- 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>
❓ أسئلة شائعة
definePageMeta.defineNuxtRouteMiddleware بحد ذاته لا يدعم async. ومع ذلك، يمكنك استدعاء واجهات برمجة متزامنة (مثل useCookie أو useState) داخل الوسيط. إذا كنت بحاجة إلى إجراء تحقق غير متزامن، يُنصح بمعالجته في إضافة أو في طريقة setup للصفحة.createError في الوسيط؟error.vue.useState أو خاصية payload في NuxtApp. const nuxtApp = useNuxtApp() ← nuxtApp.payload.xxx = data. يمكن للوسيط أو الصفحة التالية الوصول إليها.📖 ملخص
- ثلاثة أنواع من الوسيط: عام (جميع المسارات)، ومُسمّى (صفحات محددة)، ومضمن (الصفحة الحالية)
- الوسيط العام يُنفَّذ بترتيب أبجدي حسب اسم الملف؛ يُنصح باستخدام بادئة رقمية للتحكم في الترتيب
- يمكن الجمع بين عدة وسطاء مُسمّيين باستخدام مرجع مصفوفة
middlewareفيdefinePageMeta - التنفيذ المزدوج في SSR هو سلوك متوقع؛ يجب حماية واجهة برمجة المتصفح باستخدام
import.meta.client - MegaShop ينفذ حماية صلاحيات كاملة باستخدام مصادقة عامة ووسيط admin مُسمّى
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): أنشئ وسيطًا باسم
guest.tsيسمح فقط للمستخدمين غير المصادق عليهم بالوصول إلى الصفحة (يتم إعادة توجيه المستخدمين المسجلين إلى الصفحة الرئيسية)، وطبّقه على صفحة/login. - تمرين متقدم (الصعوبة: ⭐⭐): نفّذ نظام ترخيص كامل: فحص مصادقة عام لتسجيل الدخول + فحص دور المستخدم "admin"؛ مسار /admin/** محمي.
- تحدٍ (الصعوبة: ⭐⭐⭐): نفّذ تمرير البيانات بين الوسطاء—استرجع معلومات المستخدم في الوسيط
authوخزّنها في حمولةNuxtApp، ثم اقرأها مباشرة في الوسيطadminلتجنب الطلبات المكررة
---|



