Skip to content

16. مثال [nuxt-13]: التحقق من التنقل في [nuxt-12]

في هذا المثال، نركز على التنقل في [nuxt-12]. لم نقم بذلك في [nuxt-12] لأن التحكم في التنقل كان سيضيف تعقيدًا إلى مثال معقد بالفعل.

الهدف: نريد أن يتمكن المستخدم من تنفيذ الإجراءات المصرح بها فقط:

  • إذا لم تبدأ جلسة JSON، يُسمح فقط بعنوان URL [/]؛
  • إذا بدأت جلسة JSON ولكن لم يتم توثيق المستخدم، فسيُسمح فقط بعنوان URL [/authentication
  • إذا بدأت جلسة JSON وتم توثيق المستخدم، فسيتم السماح فقط بعناوين URL [/get-admindata, /end-session
  • عندما لا يكون هدف التوجيه الحالي مصرحًا به، سيتم إعادة التوجيه إلى عنوان URL مصرح به؛

يتم الحصول على المثال [nuxt-13] في البداية عن طريق نسخ المثال [nuxt-12]:

Image

سيتم إجراء التغييرات في مجلد التوجيه [middleware].

16.1. توجيه تطبيق [nuxt]

يتم تكوين توجيه التطبيق على النحو التالي في ملف [nuxt.config]:


// router
  router: {
    // application URL root
    base: '/nuxt-13/',
    // routing middleware
    middleware: ['routing']
},
  • السطر 6: يتم التحكم في توجيه التطبيق بواسطة ملف [middleware/routing

ملف [middleware/routing] هو كما يلي:


/* eslint-disable no-console */
 
// on importe les middleware du serveur et du client
import serverRouting from './server/routing'
import clientRouting from './client/routing'
 
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware], process.server', process.server, ', process.client=', process.client)
  if (process.server) {
    // routage serveur
    serverRouting(context)
  } else {
    // routage client
    clientRouting(context)
  }
}
  • الأسطر 10–16: يتم التعامل مع توجيه [nuxt] للعميل والخادم بشكل مختلف. وهذا هو أحد أهم نقاط الاختلاف بينهما؛
  • السطر 4: يتم تنفيذ توجيه الخادم بواسطة البرنامج النصي [middleware/server/routing
  • السطر 5: يتم تنفيذ توجيه العميل بواسطة البرنامج النصي [middleware/client/routing

16.2. توجيه [nuxt] على جانب العميل

يظل توجيه جانب العميل [nuxt] كما كان في [nuxt-12]:


/* eslint-disable no-console */
export default function(context) {
  // who executes this code?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // management of the PHP session cookie in the browser
  // the browser's PHP session cookie must be identical to the one found in the nuxt session
  // acion [fin-session] receives a new cookie PHP (server as nuxt client)
  // if the server receives it, the client must pass it on to the browser
  // for its own exchanges with the PHP server
  // this is customer routing
 
  // retrieve the session cookie PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // if it exists, we assign the PHP session cookie to the browser
    document.cookie = phpSessionCookie
  }
 
  ...
}

لمنع العميل من الوصول إلى المسارات غير المصرح بها، سنعرض فقط المسارات المصرح بها في قائمة التنقل الخاصة بالعميل. يصبح مكون [components/navigation] كما يلي:


<template>
  <!-- bootstrap menu with three options -->
  <b-nav vertical>
    <b-nav-item v-if="$store.state.jsonSessionStarted && !$store.state.userAuthenticated" to="/authentification" exact exact-active-class="active">
      Authentification
    </b-nav-item>
    <b-nav-item
      v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated && !$store.state.adminData"
      to="/get-admindata"
      exact
      exact-active-class="active"
    >
      Requête AdminData
    </b-nav-item>
    <b-nav-item v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated" to="/fin-session" exact exact-active-class="active">
      Fin session impôt
    </b-nav-item>
  </b-nav>
</template>
  • السطر 4: لا يتوفر خيار [Authentication] إلا إذا كانت جلسة JSON قد بدأت ولكن لم تتم مصادقة المستخدم. إذا لم تبدأ جلسة JSON أو تمت مصادقة المستخدم بالفعل، فلن يتوفر الخيار؛
  • الأسطر 7-11: لا يتوفر خيار [AdminData Request] إلا إذا بدأت جلسة JSON وتم مصادقة المستخدم ولم يتم استرداد [AdminData] بعد. إذا لم يتم استيفاء أي من هذه الشروط الثلاثة (لم تبدأ جلسة JSON، أو لم تتم مصادقة المستخدم، أو تم استرداد بيانات [AdminData] بالفعل)، فلن يتم عرض الخيار؛
  • السطر 15: يتوفر خيار [End Tax Session] بمجرد بدء جلسة JSON وتوثيق المستخدم؛ وإلا، فلن يكون متاحًا؛

16.3. توجيه الخادم [Nuxt]

يعد توجيه الخادم عمومًا أكثر تعقيدًا من التوجيه من جانب العميل لأن المستخدم يمكنه كتابة أي عنوان URL في شريط عنوان متصفحه. يمكننا السماح بحدوث ذلك (ففي النهاية، لا يُفترض أن يقوم المستخدم بذلك) أو محاولة التحكم فيه. هذا ما سنفعله هنا، من أجل هذا المثال، لأنه في حالة تطبيق [nuxt-12]، يمكننا الاستغناء عنه بسهولة لأن خادم حساب الضريبة محمي جيدًا ضد عناوين URL التي يتم إدخالها يدويًا ويعرف كيفية إرسال رسائل الخطأ المناسبة. رأينا ذلك في [next-12]، حيث لم يكن هناك تحكم في التوجيه.

يختلف التوجيه على خادم [nuxt] اختلافًا كبيرًا عن التوجيه على عميل [nuxt] من حيث إعادة التوجيه:

  • عندما يتم إعادة توجيه خادم [nuxt]، فإنه يرسل طلب إعادة توجيه إلى متصفح العميل مع هدف إعادة التوجيه. ثم يقوم المتصفح بإرسال طلب جديد إلى خادم [nuxt]، يطلب فيه الهدف الذي تم إرساله إليه. وكأن المستخدم قد كتب عنوان URL لهدف إعادة التوجيه يدويًا: يتم إعادة تشغيل تطبيق [nuxt] بالكامل، ومعه دورة حياته بالكامل (مكونات الخادم الإضافية، المخزن، توجيه الخادم، الصفحات)؛
  • عندما يتم إعادة توجيه عميل [nuxt]، لا يحدث أي من هذا. هناك ببساطة تغيير في الصفحة، تمامًا كما كان سيحدث لو أن المستخدم نقر على رابط يؤدي إلى هدف إعادة التوجيه. ثم يختلف دورة الحياة (توجيه جانب العميل، عرض هدف المسار)؛

لهذا السبب، يُفضل فصل التوجيه من جانب العميل عن التوجيه من جانب الخادم، على الرغم من أن قاعدتي الكود قد تبدوان متشابهتين.

سيكون نص توجيه الخادم [middleware/server/routing] كما يلي:


/* eslint-disable no-console */
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)
 
  // on récupère quelques informations dans le store [nuxt]
  const store = context.store
  // d'où vient-on ?
  const from = store.state.from || 'nowhere'
  ...
}
  • في التوجيه من جانب العميل، تتلقى وظيفة التوجيه السياق [context] مع الخاصية [context.from]، وهي مسار الصفحة التي أتينا منها. يتم الحصول على المسار الذي سنذهب إليه عبر [context.route
  • في التوجيه من جانب الخادم، تتلقى وظيفة التوجيه السياق [context] بدون الخاصية [context.from]. لا يحدث التوجيه من جانب الخادم إلا عندما يُطلب عنوان URL يدويًا من خادم [nuxt]. ونعلم أن تطبيق [nuxt] بأكمله يتم إعادة تعيينه عندئذٍ. وكأننا نبدأ من الصفر، لذا لا يوجد مفهوم "الصفحة السابقة"؛
  • بفضل جلسة [nuxt]، نعلم أن الخادم يمكنه استرداد هذه الجلسة وبالتالي تجنب البدء من الصفر. لذلك، في جلسة [nuxt] هذه، وبشكل أكثر تحديدًا في مخزن الجلسة، سنقوم بتخزين اسم آخر صفحة عرضها متصفح العميل قبل طلب عنوان URL من خادم [nuxt
  • الأسطر 7-9: نسترد اسم آخر صفحة عرضها متصفح العميل. عند بدء تشغيل التطبيق، لا توجد هذه المعلومات [from] في المخزن. ثم نعيّن الاسم [nowhere] للمتغير [from

لكي يتمكن خادم [nuxt] من استرداد اسم آخر صفحة عرضها متصفح العميل من المخزن، يجب على عميل [nuxt] أيضًا إدخال هذه المعلومات في المخزن. وبالتالي، يتم استكمال برنامج توجيه عميل [nuxt] على النحو التالي:


/* eslint-disable no-console */
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // gestion du cookie de la session PHP dans le navigateur
  // le cookie de la session PHP du navigateur doit être identique à celui trouvé en session nuxt
  // l'acion [fin-session] reçoit un nouveau cookie PHP (serveur comme client nuxt)
  // si c'est le serveur qui le reçoit, le client doit le transmettre au navigateur
  // pour ses propres échanges avec le serveur PHP
  // on est ici dans un routing client
 
  // on récupère le cookie de la session PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // s'il existe, on affecte le cookie de session PHP au navigateur
    document.cookie = phpSessionCookie
  }
 
  // on met dans la session le nom de la page où on va - pas de redirection serveur
  context.store.commit('replace', { serverRedirection: false, from: context.route.name })
  // on sauvegarde le store dans la session [nuxt]
  const session = context.app.$session()
  session.value.store = context.store.state
  session.save(context)
}
  • تمت إضافة الأسطر 19–24؛
  • السطر 20: نقوم بتخزين اسم الصفحة [context.route.name] التي سيتم عرضها في المخزن، والتي ستكون بمثابة الصفحة التي أتينا منها خلال خطوة التوجيه التالية. علاوة على ذلك، سنرى أنه في توجيه خادم [nuxt]، يجب أن يعرف ما إذا كان التوجيه الحالي ناتجًا عن إعادة توجيه سابقة بواسطة خادم [nuxt]. هنا، ليس هذا هو الحال، لذا نضبط الخاصية [serverRedirection] على [false
  • الأسطر 22–24: يتم وضع حالة المخزن في جلسة [nuxt] (السطر 23)، ثم يتم حفظ جلسة [nuxt] في ملف تعريف ارتباط (السطر 24)، والذي بدوره سيتم حفظه في متصفح عميل [nuxt

لنعد إلى نصوص توجيه خادم [nuxt]:


/* eslint-disable no-console */
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)
 
  // on récupère quelques informations dans le store [nuxt]
  const store = context.store
  // d'où vient-on ?
  const from = store.state.from || 'nowhere'
  // où va-t-on ?
  const to = context.route.name
  // éventuelle redirection
  let redirection = ''
  // gestion du routage terminé
  let done = false
 
  // est-on déjà dans une redirection du serveur [nuxt]?
  if (store.state.serverRedirection) {
    // rien à faire
    done = true
  }
 
  // est-ce un rechargement de page ?
  if (to === from) {
    // rien à faire
    done = true
  }
 
  // contrôle de la navigation du serveur [nuxt]
  // on s'inspire de la navigation client dans le composant [navigation]
 
  // cas où la session PHP n'a pas démarré
  if (!done && !store.state.jsonSessionStarted && to !== 'index') {
    // redirection
    redirection = 'index'
    // travail terminé
    done = true
  }
 
  // cas où l'utilisateur n'est pas authentifié
  if (!done && store.state.jsonSessionStarted && !store.state.userAuthenticated && to !== 'authentification') {
    // redirection
    redirection = from
    // travail terminé
    done = true
  }
 
  // cas où l'utilisateur a été authentifié
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && to !== 'get-admindata' && to !== 'fin-session') {
    // on reste sur la même page
    redirection = from
    // travail terminé
    done = true
  }
 
  // cas où [adminData] a été obtenu
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && store.state.adminData && to !== 'fin-session') {
    // on reste sur la même page
    redirection = from
    // travail terminé
    done = true
  }
 
  // on a fait tous les contrôles ---------------------
  // redirection ?
  if (redirection) {
    // on note la redirection dans le store
    store.commit('replace', { serverRedirection: true })
  } else {
    // pas de redirection
    store.commit('replace', { serverRedirection: false, from: to })
  }
  // on sauvegarde le store dans la session [nuxt]
  const session = context.app.$session()
  session.value.store = store.state
  session.save(context)
  // on fait l'éventuelle redirection
  if (redirection) {
    context.redirect({ name: redirection })
  }
}
  • الأسطر 6–9: استرداد قيمة [from] من مخزن خادم [nuxt
  • السطر 11: نسجل وجهة المسار الحالي؛
  • السطر 13: قد يؤدي التوجيه إلى إعادة توجيه متصفح العميل. وستكون [redirection] هي وجهة إعادة التوجيه هذه؛
  • السطر 15: تعيين [done] إلى [true] يشير إلى اكتمال التوجيه؛
  • الأسطر 17–21: أولاً، نتحقق مما إذا كان التوجيه الحالي ناتجًا عن طلب إعادة توجيه تم إرساله إلى متصفح العميل. يتم تخزين هذه المعلومات في خاصية [serverRedirection] في المخزن. إذا كانت هذه الخاصية صحيحة، فإن خادم [nuxt] أرسل إعادة توجيه إلى متصفح العميل أثناء الطلب السابق إلى خادم [nuxt]. في هذه الحالة، لا يلزم إجراء أي توجيه. أثناء الطلب السابق، قرر موجه خادم [nuxt] أنه يجب إعادة توجيه متصفح العميل. لا يلزم تجاوز هذا القرار بتوجيه جديد؛
  • الأسطر 23-27: نتحقق مما إذا كان التوجيه الحالي عبارة عن إعادة تحميل للصفحة. إذا كان الأمر كذلك، فإننا نسمح له بالمتابعة؛
  • ابتداءً من السطر 29، نستأنف القواعد المطبقة في مكون [navigation] لعميل [nuxt] (انظر الفقرة السابقة)؛
  • الأسطر 32–38: نتعامل مع الحالة التي لم تبدأ فيها جلسة JSON وهدف التوجيه ليس الصفحة [index]. في هذه الحالة، نعيد توجيه متصفح العميل إلى الصفحة [index
  • الأسطر 40–46: نتعامل مع الحالة التي تكون فيها جلسة JSON قد بدأت، ولم يتم توثيق المستخدم، وهدف التوجيه الحالي ليس صفحة [authentication]. في هذه الحالة، نرفض التوجيه ونبقى حيث كنا؛
  • الأسطر 48–54: تعالج الحالة التي تكون فيها جلسة JSON قد بدأت، والمستخدم قد تمت مصادقته، وهدف التوجيه الحالي ليس صفحة [get-admindata] ولا صفحة [end-session]، وهما الوجهتان الوحيدتان المحتملتان. في هذه الحالة، يتم رفض التوجيه المطلوب، ونعود إلى حيث كنا سابقًا؛
  • الأسطر 56–62: نتعامل مع الحالة التي تم فيها الحصول على [adminData]. في هذه الحالة، يوجد هدف توجيه واحد ممكن فقط: صفحة [fin-session]. إذا لم تكن تلك هي الصفحة المطلوبة، فإننا نرفض التوجيه ونعود إلى حيث كنا سابقًا؛
  • الأسطر 64-72: إذا حدثت إعادة توجيه، نقوم بتسجيلها في مخزن خادم [nuxt]: [serverRedirection: true]. لاحظ أننا لا نعين قيمة لخاصية [from] في المخزن. والسبب هو أنه ستكون هناك إعادة توجيه من متصفح العميل، وقد رأينا أنه في هذه الحالة، لم يكن هناك توجيه (الأسطر 17–20) ولا تُستخدم خاصية [from] في المخزن؛
  • الأسطر 66–69: إذا لم تكن هناك إعادة توجيه، يتم تدوين ذلك أيضًا في مخزن خادم [nuxt]: [serverRedirection: false]. بالإضافة إلى ذلك، سيعرض التوجيه الحالي الصفحة [to]، والتي ستصبح الصفحة السابقة للطلب التالي (العميل أو خادم [nuxt]). لهذا السبب نكتب [from: to
  • الأسطر 73-76: نقوم بحفظ المخزن في جلسة [nuxt]، والتي يتم حفظها بدورها في ملف تعريف ارتباط؛
  • الأسطر 77–80: إذا لم يكن [redirection] فارغًا، فإننا نوجه المتصفح لإعادة التوجيه. خلاف ذلك (غير موضح هنا)، ستستمر دورة حياة خادم [nuxt]: ستتم معالجة الصفحة [to] بواسطة خادم [nuxt] وإرسالها إلى متصفح عميل [nuxt] مع ملف تعريف ارتباط جلسة عمل [nuxt

التوجيه المختار هنا لخادم [nuxt] هو اختيار تعسفي. كان بإمكاننا اختيار توجيه آخر أو، كما ذكرنا، عدم استخدام التوجيه على الإطلاق. يتميز التوجيه المختار أعلاه بميزة الحفاظ دائمًا على التطبيق في حالة مستقرة بغض النظر عن عنوان URL الذي يطلبه المستخدم.

يمكننا تحسين هذا قليلاً عندما تكون الصفحة التي يتم تحميلها في النهاية هي الصفحة الأصلية. هناك حالتان:

  • قام المستخدم بتشغيل إعادة تحميل الصفحة (to===from
  • وجود عمليات إعادة توجيه إلى الصفحة الأصلية (redirection===from

في كلتا الحالتين، سيتم تنفيذ الصفحة الأصلية مرة أخرى مع استدعاءها غير المتزامن لخادم حساب الضرائب. لنأخذ مثالاً. إذا قام المستخدم، بعد المصادقة، بإعادة تحميل الصفحة (F5). في هذه الحالة، في التوجيه أعلاه، لدينا: [to]=[from]=[authentication]. لا توجد إعادة توجيه. سيتم تنفيذ الصفحة [to=authentication] بواسطة خادم [nuxt]. إذا لم نفعل شيئًا، فستعمل الدالة [asyncData] مرة أخرى. وهذا غير ضروري لأن المصادقة قد تمت بالفعل.

يمكننا تحسين ذلك عن طريق تعديل صفحة [authentication] قليلاً:


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[authentification asyncData started]')
    // don't do things twice if the page has already been requested
    if (process.server && context.store.state.userAuthenticated) {
      console.log('[authentification asyncData canceled]')
      return { result: '[succès]' }
    }
      // customer [nuxt]
    if (process.client) {
      // start waiting
      context.app.$eventBus().$emit('loading', true)
      // no error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // authenticate to the server
...
  • الأسطر 6-9: إذا كانت الصفحة تُقدم بواسطة خادم [nuxt] ووجدنا في المخزن أن المصادقة قد تمت بالفعل، فإننا نُرجع النتيجة المطلوبة مباشرةً (السطر 8)؛

نقوم بنفس الشيء لجميع الصفحات:

الصفحة [index]:


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[index asyncData started]')
    // don't do things twice if the page has already been requested
    if (process.server && context.store.state.jsonSessionStarted) {
      console.log('[index asyncData canceled]')
      return { result: '[succès]' }
    }
    try {
...

الصفحة [get-admindata]


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[get-admindata asyncData started]')
    // don't do things twice if the page has already been requested
    if (process.server && context.store.state.adminData) {
      console.log('[get-admindata asyncData canceled]')
      return { result: context.store.state.adminData }
    }
    // customer
    if (process.client) {
      // start waiting
      context.app.$eventBus().$emit('loading', true)
      // no error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   ...  

الصفحة [إنهاء الجلسة]


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[fin-session asyncData started]')
    // don't do things twice if the page has already been requested
    if (process.server && context.store.state.jsonSessionStarted && !context.store.state.userAuthenticated) {
      console.log('[fin-session asyncData canceled]')
      return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
    }
    // customer case [nuxt]
    if (process.client) {
      // start waiting
      context.app.$eventBus().$emit('loading', true)
      // no error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
 

16.4. التنفيذ

لتشغيل هذا المثال، تأكد من حذف ملف تعريف الارتباط الخاص بجلسة [nuxt] وملف تعريف الارتباط الخاص بـ PHP من المتصفح الذي يشغل عميل [nuxt] قبل التنفيذ لبدء من صفحة بيضاء. فيما يلي مثال باستخدام متصفح Chrome:

Image

16.5. الخلاصة

يعد التوجيه على خادم [nuxt] أمرًا معقدًا لأنه يجب عليك توقع كل عنوان URL قد يكتبه المستخدم يدويًا. هذا مثال نموذجي. لا يُقصد استخدام تطبيق [nuxt] بهذه الطريقة. بمجرد تقديم صفحة [index] بواسطة موجه خادم [nuxt]، يمكن إعادة توجيه الطلبات اللاحقة الموجهة إلى الخادم إلى صفحة خطأ.

في الحالة المحددة لمثال [nuxt-13] الخاص بنا، لم يكن توجيه خادم [nuxt] ضروريًا. كان السلوك الافتراضي (الذي لا يتضمن توجيهًا بشكل أساسي) في مثال [nuxt-12] يعمل بشكل جيد تمامًا.