Skip to content

15. مثال [nuxt-12]: HTTP درخواست‌ها را با استفاده از axios ارسال می‌کند

15.1. Présentation

در این مثال جدید، بررسی خواهیم کرد که چگونه در داخل توابع [asyncData] می‌توانیم با استفاده از کتابخانه [axios] درخواست‌های HTTP را ارسال کنیم. علاوه بر این، مفاهیمی را که قبلاً آموخته‌ایم به کار خواهیم برد:

  • استفاده از پلاگین‌ها از مثال [nuxt-06]:
  • ذخیره کردن فروشگاه در یک کوکی جلسه، همانطور که در مثال [nuxt-06] نشان داده شده است؛
  • کنترل ناوبری با استفاده از میان‌افزار (middleware) از مثال [nuxt-09
  • مدیریت خطا از مثال [nuxt-11

معماری مثال به شرح زیر خواهد بود:

Image

  • برنامه کاربردی [nuxt] بر روی سرور [node.js] [3] ذخیره خواهد شد، توسط مرورگر [1] دانلود شده و سپس اجرا می‌گردد؛
  • هم کلاینت [nuxt] [1] و هم سرور [nuxt] [3] درخواست‌هایی HTTP به سرور داده QZXW ارسال خواهند کرد.2HTMLP001453ZQX. این سرور، سرور محاسبه مالیات توسعه‌یافته در بخش PHP 7 خواهد بود. ما از آخرین نسخه آن، نسخه 14، با درخواست‌های CORS مجاز استفاده خواهیم کرد؛

معماری این مثال را می‌توان به صورت زیر ساده کرد:

Image

  • در [1]، سرور [node.js] صفحات [nuxt] را به مرورگر [2] تحویل می‌دهد. این لایه [web] [8] سرور است که این صفحات را تحویل می‌دهد. برای تحویل صفحه، ممکن است سرور از سرور داده [3] درخواست داده خارجی کرده باشد. این لایه [DAO]–[9] است که درخواست‌های لازم HTTP را انجام می‌دهد؛
  • با هر درخواست صفحه به سرور [node.js][1]، مرورگر [2] کل برنامه [nuxt] را دریافت می‌کند که سپس در حالت SPA اجرا می‌شود. بلوک [UI] (رابط کاربری) [4] صفحات [vue.js] را به کاربر نمایش می‌دهد. اقدامات انجام‌شده در این بلوک یا در چرخهٔ طبیعی صفحات ممکن است درخواست‌های داده‌ای به سرور داده [3] را تحریک کند. این لایه [DAO] [5] است که سپس درخواست‌های لازم HTTP را انجام می‌دهد؛

15.2. ساختار پروژه

Image

15.3. فایل پیکربندی [nuxt.config.js]

این پروژه توسط فایل زیر کنترل خواهد شد: [nuxt.config.js]


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    title: 'Introduction à [nuxt.js]',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: 'ssr routing loading asyncdata middleware plugins store'
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: false,

  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [
    { src: '@/plugins/client/plgSession', mode: 'client' },
    { src: '@/plugins/server/plgSession', mode: 'server' },
    { src: '@/plugins/client/plgDao', mode: 'client' },
    { src: '@/plugins/server/plgDao', mode: 'server' },
    { src: '@/plugins/client/plgEventBus', mode: 'client' }
  ],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // مستندات: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    // https://www.npmjs.com/package/cookie-universal-nuxt
    'cookie-universal-nuxt'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) { }
  },
  // پوشهٔ کد منبع
  srcDir: 'nuxt-12',
  // روتر
  router: {
    // ریشهٔ برنامه
    base: '/nuxt-12/',
    // میان‌افزار مسیریابی
    middleware: ['routing']
  },
  // سرور
  server: {
    // پورت سرویس، به‌طور پیش‌فرض 3000
    port: 81,
    // آدرس‌های شبکه‌ای که گوش فرا می‌دهند، پیش‌فرض localhost: 127.0.0.1
    // 0.0.0.0 = تمام آدرس‌های شبکه روی دستگاه
    host: 'localhost'
  },
  // محیط
  env: {
    //پیکربندی axios
    timeout: 2000,
    withCredentials: true,
    baseURL: 'http://'localhost/php7/scripts-web/impots/version-14',
    // تنظیمات کوکی جلسه [nuxt]
    maxAge: 60 * 5
  }
}
  • خط ۲۲: ما خودمان هشدار «در انتظار تکمیل اقدام غیرهم‌زمان» را مدیریت می‌کنیم؛
  • خط ۳۱: ما از پلاگین‌های مختلفی استفاده خواهیم کرد که به‌طور خاص برای کلاینت یا سرور طراحی شده‌اند، اما نه برای هر دو به‌طور همزمان؛
  • خط ۵۲: ماژول [axios] در [nuxt] ادغام شده است. در نتیجه، شیء [axios]، که درخواست‌های HTTP را ازبرنامه [nuxt] برای سرور محاسبه مالیات PHP در [context.$axios] در دسترس خواهد بود؛
  • خط ۵۴: ماژول [cookie-universal-nuxt] به ما امکان می‌دهد جلسه [nuxt] را در یک کوکی ذخیره کنیم؛
  • خط ۶۰: خاصیت [axios] به ما امکان می‌دهد ماژول [@nuxtjs/axios] را در خط ۵۲ پیکربندی کنیم. ما از این گزینه استفاده نخواهیم کرد و در عوض خاصیت [env] را در خط ۸۸ ترجیح می‌دهیم؛
  • خط ۹۰: حداکثر زمان انتظار برای پاسخ از سرور محاسبه مالیات؛
  • خط ۹۱: مورد نیاز برای کلاینت [nuxt] – استفاده از کوکی‌ها را در ارتباطات با سرور محاسبه مالیات مجاز می‌سازد؛
  • خط ۹۲: URL پایه برای سرور محاسبه مالیات؛
  • خط ۹۴: طول عمر جلسه Nuxt (۵ دقیقه);
  • خط ۷۷: ترافیک کلاینت و سرور ([nuxt]) توسط میان‌افزار مسیریابی کنترل خواهد شد؛

15.4. لایه [UI] برنامه

Image

ما به برنامه [nuxt] دسترسی به API را بر روی سرور محاسبه مالیات از طریق نمای زیر اعطا خواهیم کرد:

Image

  • در [2]، منویی که دسترسی به API را روی سرور محاسبه مالیات فراهم می‌کند:
    • [Authentification]: معادل صفحه [authentification] است. این صفحه با استفاده از اعتبارنامه‌های [admin, admin]، که در حال حاضر تنها اعتبارنامه‌های مجاز هستند، یک درخواست احراز هویت به سرور محاسبه مالیات ارسال می‌کند. نتیجه نمایش داده شده مشابه نتیجه [3] است؛
    • [Requête AdminData]: با صفحه [get-admindata] مطابقت دارد. این صفحه داده‌هایی را از سرور محاسبه مالیات – که در اینجا به آن [adminData] گفته می‌شود – درخواست می‌کند که امکان محاسبه مالیات را فراهم می‌آورد. نتیجه نمایش‌داده‌شده مشابه [3] است؛
    • [Fin session impôt]: مربوط به صفحه [fin-session] است. این صفحه یک درخواست پایان جلسه (PHP) را به سرور محاسبه مالیات ارسال می‌کند. سپس سرور جلسهٔ جاری PHP را خاتمه می‌دهد و یک جلسهٔ جدید و خالی را راه‌اندازی می‌کند؛

15.5. لایه‌های [dao] از برنامه [nuxt]

همان‌طور که در بالا اشاره شد، معماری برنامه [nuxt] به شرح زیر خواهد بود:

Image

  • در [1]، سرور [node.js] صفحات [nuxt] را به مرورگر [2] تحویل می‌دهد. این لایه [web] [8] سرور است که این صفحات را تحویل می‌دهد. برای تحویل صفحه، ممکن است سرور از سرور داده [3] درخواست داده خارجی کرده باشد. این لایه [DAO] [9] است که درخواست‌های لازم HTTP را انجام می‌دهد؛
  • با هر درخواست صفحه به سرور [node.js][1]، مرورگر [2] کل برنامه [nuxt] را دریافت می‌کند که سپس در حالت SPA اجرا می‌شود. بلوک [UI] (رابط کاربری) [4] صفحات [vue.js] را به کاربر نمایش می‌دهد. اقدامات درون این بلوک یا در چرخهٔ عمر صفحه ممکن است فراخوانی داده‌های خارجی به سرور دادهٔ [3] را تحریک کنند. این لایه [DAO] [5] است که سپس درخواست‌های لازم HTTP را انجام می‌دهد؛

ما از نسخهٔ ۱۴ سرور محاسبهٔ مالیات که در سند |Introduction to the PHP7 language through examples| توسعه یافته است، استفاده خواهیم کرد. ما تنها بخشی از رابط برنامه‌نویسی کاربردی (API) آن، یعنی API و jSON، را به کار خواهیم برد:

درخواست
پاسخ
1
2
3
4
5
6
[initialisation d’une session jSON avec le
serveur de calcul de l’impôt]
[une session PHP est créée avec le serveur de
calcul de l’impôt]

GET main.php?action=init-session&type=json

{
    "action": "init-session",
    "état"700,
    "réponse": "session démarrée avec type [json]"
}

[authentification de l’utilisateur]
[l’authentification est stockée dans la
session PHP]

POST main.php?action=authentifier-utilisateur
paramètres postés : user, admin

{
    "action": "authentifier-utilisateur",
    "état"200,
    "réponse": "Authentification réussie [admin, admin]"
}

[demande des données de l’administration
fiscale]
[les données reçues sont stockées dans la
session PHP]

GET main.php?action=get-admindata

{
    "action": "get-admindata",
    "état"1000,
    "réponse": {
        "limites": [
            9964,
            27519,
            73779,
            156244,
            0
        ],
        "coeffR": [
            0,
            0.14,
            0.3,
            0.41,
            0.45
        ],
        "coeffN": [
            0,
            1394.96,
            5798,
            13913.69,
            20163.45
        ],
        "plafondQfDemiPart": 1551,
        "plafondRevenusCelibatairePourReduction": 21037,
        "plafondRevenusCouplePourReduction": 42074,
        "valeurReducDemiPart": 3797,
        "plafondDecoteCelibataire": 1196,
        "plafondDecoteCouple": 1970,
        "plafondImpotCouplePourDecote": 2627,
        "plafondImpotCelibatairePourDecote": 1595,
        "abattementDixPourcentMax": 12502,
        "abattementDixPourcentMin": 437
    }
}

[fin de la session PHP avec le serveur de
calcul de l’impôt]
[supprime la session PHP courante et crée une
nouvelle session PHP. Dans celle-ci, la
session jSON reste activée, mais l’utilisateur
n’est plus authentifié]

GET main.php?action=fin-session

{
    "action": "fin-session",
    "état"400,
    "réponse": "session supprimée"
}

15.5.1. لایه [dao] از سرور [nuxt]

Image

سرور [node.js] [1] از لایه [dao] که در سند |Introduction to the VUE.JS framework through examples| توضیح داده شده است، استفاده خواهد کرد. ما کد آن را در اینجا بازتولید می‌کنیم:


'use strict';

//وارداتی
import qs from 'qs'

class Dao {

  // سازنده
  constructor(axios) {
    this.axios = axios;
    // کوکی جلسه
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
  }

  // راه‌اندازی جلسه
  async  initSession() {
    // گزینه‌های درخواست HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      //URL پارامترها
      params: {
        action: 'init-session',
        type: 'json'
      }
    };
    //اجرای پرس‌وجو HTTP
    return await this.getRemoteData(options);
  }

  async  authentifierUtilisateur(user, password) {
    // گزینه‌های درخواست HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        'Content-type': 'application/x-www-form-urlencoded',
      },
      // بدنهٔ POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      // پارامترهای URL
      params: {
        action: 'authentifier-utilisateur'
      }
    };
    // اجرای پرس‌وجو HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // گزینه‌ها برای پرس‌وجوی HHTP [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      //پارامترها برای URL
      params: {
        action: 'get-admindata'
      }
    };
    // اجرای پرس‌وجو HTTP
    const data = await this.getRemoteData(options);
    // نتیجه
    return data;
  }

  async  getRemoteData(options) {
    //برای کوکی جلسه
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    //اجرای درخواست HTTP
    let response;
    try {
      //درخواست ناهمزمان
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // پارامتر [error] یک نمونه استثنا است – می‌تواند اشکال مختلفی داشته باشد
      if (error.response) {
        //پاسخ سرور در [error.response] است
        response = error.response;
      } else {
        //خطا مجدداً فعال می‌شود
        throw error;
      }
    }
    //پاسخ، کل پاسخ HTTP از سرور است (سربرگ‌های HTTP + خود پاسخ)
    //کوکی جلسه را در صورت وجود بازیابی می‌کند
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie یک آرایه است
      //در این آرایه به دنبال کوکی جلسه می‌گردد
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        //جستجو برای کوکی جلسه
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // کوکی جلسه ذخیره می‌شود
          //eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // یافت شد
          trouvé = true;
        } else {
          //عنصر بعدی
          i++;
        }
      }
    }
    //پاسخ سرور در [response.data] است
    return response.data;
  }
}

//کلاس صادرشده
export default Dao;
  • تمام متدهای لایه [dao]، شیء ارسال‌شده توسط سرور داده [{action : ‘xx’, état : nn, réponse : {...}] را با: بازمی‌گردانند
    • [action]: نام عملی که توسط سرور داده اجرا می‌شود؛
    • [état]: شاخص عددی:
      • [initSession]: وضعیت=700 برای پاسخی بدون خطا؛
      • [authentifierUtilisateur]: وضعیت=200 برای یک پاسخ بدون خطا؛
      • [getAdminData]: وضعیت=1000 برای یک پاسخ بدون خطا؛
      • [fin-session]: وضعیت=400 برای پاسخی بدون خطا؛
    • [réponse]: پاسخی مرتبط با شاخص عددی [état]. ممکن است بسته به این شاخص عددی متفاوت باشد؛

بیایید سازنده کلاس [Dao] را بررسی کنیم:


// سازنده
  constructor(axios) {
    this.axios = axios;
    // کوکی جلسه
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
}
  • خط ۲: ابجکت [axios] که به‌عنوان آرگومان به سازنده ارسال می‌شود، توسط کد فراخوانی‌کننده فراهم می‌گردد. این ابجکت است که درخواست‌های HTTP را انجام خواهد داد؛
  • خط ۵: نام کوکی جلسه ارسال‌شده توسط سرور داده، نوشته شده به صورت PHP;
  • خط ۶: کوکی جلسه که بین لایه [dao] و سرور داده مبادله می‌شود. این کوکی توسط تابع [getRemoteData] در خطوط ۶۷–۱۱۳ مقداردهی اولیه می‌شود؛

برای کوکی جلسه، باید دو لایهٔ جداگانه را در نظر بگیریم، [dao]:

  • لایه مرورگر؛
  • لایهٔ سرور؛

ما باید سه کوکی جلسه را مدیریت کنیم:

  1. آن که بین کلاینت [nuxt] و سرور PHP 7 مبادله می‌شود؛
  2. یکی که بین سرور [nuxt] و سرور PHP 7 مبادله می‌شود؛
  3. cookie جلسه‌ای که بین کلاینت [nuxt] و سرور [nuxt] مبادله می‌شود؛

ما اطمینان حاصل خواهیم کرد که کوکی جلسه برای سرور PHP برای هر دو کلاینت و سرور [nuxt] یکسان باشد. ما به این کوکی «کوکی جلسه PHP» خواهیم گفت. این همان کوکی است که در موارد ۱ و ۲ استفاده می‌شود. ما به کوکی مورد ۳ «کوکی جلسه [nuxt]» خواهیم گفت. بنابراین دو جلسه خواهیم داشت:

  • یک جلسه با نام PHP و کوکی جلسه PHP؛
  • یک جلسه [nuxt] با کوکی جلسه [nuxt

چرا از همان کوکی برای جلسهٔ مشتری PHP و جلسهٔ مرورگر [nuxt] استفاده کنیم؟ ما می‌خواهیم برنامه بتواند با سرور PHP ارتباط برقرار کند، صرف‌نظر از اینکه آن سرور، سرور مشتری باشد یا سرور [nuxt]:

  • اگر یک اقدام A در سرور [nuxt]، سرور PHP را به حالت E تنظیم کند، این حالت در جلسه PHP که توسط سرور PHP نگهداری می‌شود، منعکس می‌گردد؛
  • با استفاده از همان کوکی جلسه PHP مانند سرور، یک اقدام B توسط کلاینت [nuxt] پس از اقدام A توسط سرور [nuxt]، سرور PHP را در ... پیدا خواهد کرد.وضعیت E که توسط سرور [nuxt] به جا گذاشته شده است و بنابراین می‌تواند بر روی کار انجام شده قبلی توسط سرور [nuxt] بنا کند؛
  • اگر پس از اقدام B توسط کلاینت [nuxt]، یک اقدام C توسط سرور [nuxt] دنبال شود، به همان دلیلی که قبلاً گفته شد، این اقدام قادر خواهد بود بر روی کار انجام شده توسط اقدام B توسط کلاینت [nuxt] بنا کند؛

برای اینکه مرورگر کلاینت [nuxt] بتواند با سرور محاسبه مالیات PHP ارتباط برقرار کند، از نسخه 14 این سرور استفاده خواهیم کرد که تماس‌های بین‌دامنه‌ای را مجاز می‌سازد، یعنی تماس‌هایی از یک مرورگر به سمت سرور PHP. با این حال، تماس‌ها از سرور [nuxt] به سمت سرور PHP، تماس‌های بین‌دامنه‌ای محسوب نمی‌شوند. این مفهوم فقط برای تماس‌هایی که از یک مرورگر گرفته می‌شوند، صدق می‌کند.

بیایید به کد سازنده کلاس [Dao] قبلی بازگردیم:


// سازنده
  constructor(axios) {
    this.axios = axios;
    // cookie جلسه
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
}
  • خطوط ۵ و ۶ مربوط به کوکی جلسه PHP با سرور محاسبه مالیات هستند؛

پردازش کوکی جلسه PHP که در بالا توضیح داده شد، برای سرور [nuxt] مناسب نیست: لایه آن [dao] با هر درخواست جدیدی که به سرور [nuxt] ارسال می‌شود، نمونه‌سازی می‌شود. باید توجه داشت که درخواست یک صفحه از سرور [nuxt] عملاً برنامه [nuxt] را ریست می‌کند. بنابراین، هنگامی که، پس از اولین درخواست ارسال‌شده به سرور داده توسط سرور [nuxt]، کوکی جلسه PHP برای لایه [dao] مقداردهی اولیه می‌شود، این مقدار در درخواست بعدی HTTP از همان سرور [nuxt]، زیرا در این فاصله لایه آن [dao] مجدداً ایجاد شده، سازنده دوباره اجرا شده و کوکی جلسه PHP به یک رشته خالی بازنشانی شده است (خط ۶)؛

یک راه‌حل استفاده از یک سازنده متفاوت برای لایه [dao] سرور است:


// سازنده
  constructor(axios, phpSessionCookie) {
    // کتابخانه axios
    this.axios = axios
    // ارزش کوکی جلسه
    this.phpSessionCookie = phpSessionCookie
    // نام کوکی جلسه سرور PHP
    this.phpSessionCookieName = 'PHPSESSID'
  }
  • خط ۲: این بار، کوکی جلسه PHP به سازنده لایه [dao] سرور داده ارائه خواهد شد؛

سرور [nuxt] چگونه می‌تواند این کوکی جلسه PHP را به سازنده لایه خود [dao] ارائه دهد؟ ما کوکی جلسه PHP را در کوکی جلسه [nuxt] که بین مرورگر و سرور [nuxt] مبادله می‌شود، ذخیره خواهیم کرد. فرآیند به شرح زیر است:

  1. اپلیکیشن [nuxt] راه‌اندازی می‌شود؛
  2. هنگامی که سرور [nuxt] اولین درخواست HTTP خود را به سرور PHP ارسال می‌کند، کوکی جلسه PHP را که دریافت کرده است، در کوکی جلسه [nuxt] ذخیره می‌کند، که آن را با کلاینت [nuxt] مبادله می‌کند؛
  3. کلاینت میزبان مرورگر [nuxt] این کوکی جلسه [nuxt] را دریافت می‌کند و بنابراین به طور سیستماتیک آن را با هر درخواست جدید به سرور [nuxt] بازمی‌فرستد؛
  4. هنگامی که سرور [nuxt] نیاز به ارسال یک درخواست جدید به سرور PHP داشته باشد، کوکی جلسه PHP را در میان کوکی جلسه [nuxt] که مرورگر برای آن ارسال کرده است، پیدا خواهد کرد. سپس آن را به سرور PHP ارسال می‌کند؛

در واقع دو کوکی جلسه وجود دارد و نباید آن‌ها را با هم اشتباه گرفت:

  • کوکی جلسه [nuxt] که بین سرور [nuxt] و مرورگر کلاینت [nuxt] مبادله می‌شود؛
  • کوکی جلسه PHP که بین سرور [nuxt] و سرور PHP یا بین کلاینت [nuxt] و سرور PHP مبادله می‌شودX;

حال بیایید به کد متد در کلاس [Dao] بازگردیم. این متد فاقد تابع بستن جلسه PHP با سرور محاسبه مالیات است. ما این تابع را اضافه خواهیم کرد:


// پایان جلسه محاسبه مالیات
  async finSession() {
    // گزینه‌های درخواست HHTP  [get /main.php?action=fin-session]
    const options = {
      method: 'GET',
      //پارامترها برای URL
      params: {
        action: 'fin-session'
      }
    }
    // اجرای پرس‌وجو HTTP
    const data = await this.getRemoteData(options)
    // نتیجه
    return data
  }

در حین آزمایش، متوجه می‌شویم که تابع [getRemoteData] که در خط ۱۲ فراخوانی شده است، برای متد [finSession] مناسب نیست:


async  getRemoteData(options) {
    //برای کوکی جلسه
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    //اجرای درخواست HTTP
    let response;
    try {
      // درخواست ناهمزمان
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // پارامتر [error] یک نمونه استثنا است – می‌تواند اشکال مختلفی داشته باشد
      if (error.response) {
        //پاسخ سرور در [error.response] است
        response = error.response;
      } else {
        //خطا مجدداً فعال می‌شود
        throw error;
      }
    }
    //پاسخ، کل پاسخ HTTP از سرور است (سربرگ‌های HTTP + خود پاسخ)
    // کوکی جلسه را در صورت وجود بازیابی می‌کند
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie یک آرایه است
      // کوکی جلسه در این آرایه جستجو می‌شود
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        // جستجو برای کوکی جلسه
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // کوکی جلسه ذخیره می‌شود
          //eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // پیدا شد
          trouvé = true;
        } else {
          // عنصر بعدی
          i++;
        }
      }
    }
    //پاسخ سرور در [response.data] است
    return response.data;
  }
  • خطوط ۳۰–۴۳: سیستم به دنبال کوکی [PHPSESSID=xxx] می‌گردد. در صورت یافتن، در کلاس ذخیره می‌شود (خط ۳۶)؛

این کد برای متد جدید [finSession] مناسب نیست زیرا در اقدام [fin-session]، سرور PHP دو کوکی با نام [PHPSESSID] ارسال می‌کند. در اینجا مثالی که با کلاینت [Postman] به دست آمده است:

Image

  • در [1]، درخواست از کلاینت [Postman
  • به [3]، پاسخ از سرور PHP؛
  • در [4]، سربرگ‌های HTTP از پاسخ سرور PHP؛

Image

  • در [5]، سرور PHP ابتدا نشان می‌دهد که جلسه فعلی را حذف کرده است PHP;
  • در [6]، سرور PHP کوکی مربوط به جلسه جدید، PHP را ارسال می‌کند؛

با کد فعلی، تابع [getRemoteData] کوکی [5] را بازیابی می‌کند، در حالی که کوکی [6] باید ذخیره شود.

بنابراین کد تابع [getRemoteData] باید به شرح زیر به‌روزرسانی شود:


async getRemoteData(options) {
    //آیا کوکی سشن به نام PHP وجود دارد؟
    if (this.phpSessionCookie) {
      //آیا هیچ هدرهایی وجود دارد؟
      if (!options.headers) {
        //یک شیء خالی ایجاد می‌شود
        options.headers = {}
      }
      // سربرگ کوکی جلسه PHP
      options.headers.Cookie = this.phpSessionCookie
    }
    // اجرای درخواست HTTP
    let response
    try {
      //درخواست ناهمزمان
      response = await this.axios.request('main.php', options)
    } catch (error) {
      //پارامتر [error] یک نمونه استثنا است – می‌تواند اشکال مختلفی داشته باشد
      if (error.response) {
        //پاسخ سرور در [error.response] است
        response = error.response
      } else {
        //خطا مجدداً فعال می‌شود
        throw error
      }
    }
    //پاسخ کل پاسخ HTTP از سرور است (سربرگ‌های HTTP + خود پاسخ)
    // ما در میان کوکی‌های دریافت‌شده به دنبال کوکی جلسه PHP می‌گردیم
    // تمام کوکی‌های دریافت‌شده
    const cookies = response.headers['set-cookie']
    if (cookies) {
      //کوکی‌ها یک آرایه هستند
      //در حال جستجو برای کوکی جلسه PHP در این آرایه
      let trouvé = false
      let i = 0
      while (!trouvé && i < cookies.length) {
        //در حال جستجو برای کوکی جلسه PHP
        const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
        if (results) {
          //کوکی جلسه PHP ذخیره شده است
          const phpSessionCookie = results[1]
          //آیا حاوی رشته [deleted] است؟
          const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
          if (!results2) {
            //ما کوکی جلسه صحیح PHP را داریم
            this.phpSessionCookie = phpSessionCookie
            //یافت شد
            trouvé = true
          } else {
            //عنصر زیر
            i++
          }
        } else {
          // عنصر بعدی
          i++
        }
      }
    }
    // پاسخ سرور در [response.data] است
    return response.data
  }
  • خط ۴۱: کوکی‌ای با نام [PHPSESSID] یافت شده است. ما آن را به‌صورت محلی ذخیره می‌کنیم؛
  • خط ۴۳: بررسی می‌کنیم که آیا کوکی ذخیره شده حاوی رشته [PHPSESSID=deleted] است؛
  • خط ۴۶: اگر پاسخ خیر باشد، پس کوکی صحیح، [PHPSESSID] را پیدا کرده‌ایم. آن را در کلاس ذخیره می‌کنیم؛

پس از تابع [getRemoteData]، کوکی جلسه PHP در کلاس، در [this.phpSessionCookie] ذخیره می‌شود. ما دریافته‌ایم که کلاس با هر درخواست جدید HTTP از سرور [nuxt] نمونهسازی می‌شود. بنابراین کوکی جلسه PHP باید از کلاس استخراج شود. برای این کار، یک متد جدید به کلاس اضافه می‌کنیم:


//دسترسی به کوکی جلسه PHP
  getPhpSessionCookie() {
    return this.phpSessionCookie
}
  • سرور [nuxt] با ارسال کوکی جلسه PHP به کانستراکتور لایه [dao] خود، در صورتی که کانستراکتور داشته باشد، یک اقدام را درخواست می‌کند؛
  • پس از انجام اقدام، سرور [nuxt] کوکی جلسه PHP را که توسط لایه [dao] با استفاده از متد قبلی [getPhpSessionCookie] ذخیره شده است، بازیابی می‌کند. این کوکی ممکن است همان کوکی قبلی باشد یا کوکی متفاوتی. مورد دوم در دو موقعیت رخ می‌دهد:
    • هنگامی که متد [initSession] اجرا می‌شود (قبلاً کوکی جلسه PHP وجود نداشت)؛
    • وقتی روش [finSession] اجرا می‌شود (سرور PHP کوکی جلسه PHP را تغییر می‌دهد)؛

شایان ذکر است که یک ویژگی خاص در مورد کوکی جلسه PHP وجود دارد. سرور [nuxt] همیشه این کوکی را از سرور PHP دریافت نمی‌کند. در واقع، سرور دوم آن را فقط یک بار ارسال می‌کند. پس از آن، آن را دوباره ارسال نمی‌کند. وقتی به کد مربوط به [getRemoteData] و کد مربوط به [getPhpSessionCookie] نگاه می‌کنیم، می‌توانیم ببینیم که وقتی سرور PHP یک کوکی جلسه را ارسال نمی‌کند، تابع [getPhpSessionCookie] کوکی جلسه PHP را که به سازنده ارائه شده است، بازمی‌گرداند. به این ترتیب، سرور همیشه جدیدترین کوکی جلسه PHP را که از طرف PHP برایش ارسال شده است، به PHP بازمی‌گرداند.

15.5.2. لایه [dao] از کلاینت [nuxt]

Image

برای کلاینت [nuxt] که در یک مرورگر در حال اجرا است، ما از کد کلاس [Dao] در مستندات |مقدمه‌ای بر چارچوب VUE.JS از طریق مثال‌ها| استفاده می‌کنیم:


"use strict";

// واردات‌ها
import qs from "qs";

class Dao {
  // سازنده
  constructor(axios) {
    this.axios = axios;
  }

  // راه‌اندازی جلسه
  async initSession() {
    // گزینه‌های درخواست HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      //URL پارامترها
      params: {
        action: "init-session",
        type: "json"
      }
    };
    //اجرای پرس‌وجو HTTP
    return await this.getRemoteData(options);
  }

  async authentifierUtilisateur(user, password) {
    // گزینه‌ها برای پرس‌وجوی HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        "Content-type": "application/x-www-form-urlencoded"
      },
      // بدنه POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      //پارامترهای URL
      params: {
        action: "authentifier-utilisateur"
      }
    };
    // اجرای پرس‌وجو HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // گزینه‌ها برای پرس‌وجوی HHTP  [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      //پارامترها برای URL
      params: {
        action: "get-admindata"
      }
    };
    // اجرای پرس‌وجو HTTP
    const data = await this.getRemoteData(options);
    // نتیجه
    return data;
  }

  async getRemoteData(options) {
    //اجرای پرس‌وجوی HTTP
    let response;
    try {
      // درخواست ناهمزمان
      response = await this.axios.request("main.php", options);
    } catch (error) {
      //پارامتر [error] یک نمونه استثنا است – ممکن است اشکال مختلفی داشته باشد
      if (error.response) {
        //پاسخ سرور در [error.response] است
        response = error.response;
      } else {
        //خطا مجدداً فعال می‌شود
        throw error;
      }
    }
    //پاسخ کامل HTTP از سرور است (سربرگ‌های HTTP + خود پاسخ)
    // پاسخ سرور در [response.data] قرار دارد
    return response.data;
  }
}

// خروجی کلاس
export default Dao;

این کد با لایه [dao] سرور [nuxt] تفاوت دارد، زیرا کوکی جلسه PHP را با سرور محاسبه مالیات مدیریت نمی‌کند: این کار توسط مرورگر انجام می‌شود.

همان‌طور که برای لایه [dao] سرور [nuxt] انجام دادیم، یک متد [finSession] اضافه خواهیم کرد:


//پایان جلسه محاسبه مالیات
  async finSession() {
    // گزینه‌های پرس‌وجو HHTP  [get /main.php?action=fin-session]
    const options = {
      method: 'GET',
      //URL پارامترها
      params: {
        action: 'fin-session'
      }
    }
    // اجرای پرس‌وجو HTTP
    const data = await this.getRemoteData(options)
    // نتیجه
    return data
  }

وقتی کلاینت [nuxt] این متد را اجرا می‌کند، درست مانند سرور [nuxt]، دو کوکی جلسه PHP دریافت می‌کند. در واقع این مرورگر است که آن‌ها را دریافت می‌کند و وضعیت را به‌درستی مدیریت می‌کند: تنها کوکی مربوط به جلسه جدید، PHP، که توسط سرور محاسبه مالیات آغاز شده بود، را حفظ می‌کند. بنابراین، دفعه بعد که کلاینت [nuxt] درخواستی را به سرور PHP ارسال می‌کند، کوکی جلسه PHP صحیح خواهد بود، زیرا این مرورگر است که آن را ارسال می‌کند. با این حال، مشکلی وجود دارد: سرور [nuxt] از تغییر کوکی جلسه PHP بی‌خبر است. بنابراین در ارتباطات خود با سرور PHP، کوکی جلسه‌ای PHP را ارسال خواهد کرد که دیگر وجود ندارد، و این باعث بروز مشکلات خواهد شد. کلاینت [nuxt] باید به سرور [nuxt] اطلاع دهد که کوکی جلسه PHP تغییر کرده است و آن را به سرور منتقل کند. ما می‌دانیم چگونه این کار را انجام دهد: از طریق کوکی جلسه [nuxt]، کوکی‌ای که بین کلاینت و سرور [nuxt] مبادله می‌شود. کلاینت [nuxt] حداقل دو روش برای بازیابی کوکی جلسه جدید PHP دارد:

  1. با درخواست آن از مرورگر؛
  2. با استفاده از روش سرور [getRemoteData]، که می‌داند چگونه کوکی جلسه جدید PHP را بازیابی کند؛

ما از راه‌حل دوم استفاده خواهیم کرد زیرا آماده استفاده است. متد [getRemoteData] از کلاینت [nuxt] سپس به شکل زیر درمی‌آید:


async getRemoteData(options) {
    //اجرای پرس‌وجوی HTTP
    let response
    try {
      // درخواست ناهمزمان
      response = await this.axios.request('main.php', options)
    } catch (error) {
      //پارامتر [error] یک نمونه استثنا است – ممکن است اشکال مختلفی داشته باشد
      if (error.response) {
        //پاسخ سرور در [error.response] است
        response = error.response
      } else {
        //خطا مجدداً فعال می‌شود
        throw error
      }
    }
    //پاسخ کل پاسخ HTTP از سرور است (سربرگ‌های HTTP + خود پاسخ)
    // ما در میان کوکی‌های دریافت‌شده به دنبال کوکی جلسه PHP می‌گردیم
    // تمام کوکی‌های دریافت‌شده
    const cookies = response.headers['set-cookie']
    if (cookies) {
      //کوکی‌ها یک آرایه است
      // جستجو برای کوکی جلسه PHP در این آرایه
      let trouvé = false
      let i = 0
      while (!trouvé && i < cookies.length) {
        //در حال جستجو برای کوکی جلسه PHP
        const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
        if (results) {
          //کوکی جلسه PHP ذخیره شده است
          const phpSessionCookie = results[1]
          //آیا حاوی رشته [deleted] است؟
          const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
          if (!results2) {
            //ما کوکی جلسه صحیح PHP را داریم
            this.phpSessionCookie = phpSessionCookie
            //یافت شد
            trouvé = true
          } else {
            //عنصر زیر
            i++
          }
        } else {
          // عنصر بعدی
          i++
        }
      }
    }
    // پاسخ سرور در [response.data] است
    return response.data
  }

در [getRemoteData]، ما فقط کدی را که پاسخ از سرور PHP را برای بازیابی کوکی جلسه PHP پردازش می‌کند، حفظ کرده‌ایم. ما کدی را که کوکی جلسه PHP را در درخواست به سرور PHP قرار می‌داد، حفظ نکرده‌ایم، زیرا این کار توسط مرورگری که میزبان کلاینت [nuxt] است، انجام می‌شود.

پس از اینکه کوکی جلسه PHP توسط کلاینت [nuxt] دریافت شد، باید در جلسه [nuxt] قرار داده شود تا سرور [nuxt] بتواند از آن استفاده کند. این لایه [dao] نیست که این کار را انجام می‌دهد، اما از طریق یک متد، دسترسی به کوکی جلسه PHP را که ذخیره کرده است، فراهم می‌کند:


//دسترسی به کوکی جلسه PHP
  getPhpSessionCookie() {
    return this.phpSessionCookie
}

تابع [getPhpSessionCookie] همیشه یک کوکی جلسه معتبر بازنمی‌گرداند:

  • در اینجا مهم است که توجه داشته باشیم لایه [dao] از کلاینت [nuxt] پایدار است. این لایه یک بار ایجاد شده و سپس در حافظه باقی می‌ماند؛
  • تا زمانی که سرور PHP یک کوکی جلسه PHP را به کلاینت [nuxt] ارسال نکند، تابع [getPhpSessionCookie] در کلاینت [nuxt] مقدار [undefined] را برمی‌گرداند؛
  • وقتی سرور PHP یک کوکی جلسه PHP را به کلاینت [nuxt] ارسال می‌کند، این در [this.phpSessionCookie] ذخیره می‌شود و تا زمانی که با یک کوکی جلسه جدید، PHP، که توسط سرور PHP ارسال می‌شود، جایگزین شود، در آنجا باقی می‌ماند. سپس تابع [getPhpSessionCookie] در کلاینت [nuxt] جدیدترین کوکی جلسه، PHP، را که دریافت کرده است بازمی‌گرداند؛

لایه [dao] از کلاینت [nuxt] تنها در یک جنبه با لایه [nuxt] سرور متفاوت است: این لایه خود کوکی جلسه را ارسال نمی‌کند، زیرا این کار توسط مرورگر انجام می‌شود. با این حال، ما تصمیم گرفته‌ایم دو لایه متمایز [dao] را حفظ کنیم، زیرا دلایل پشت پیاده‌سازی‌های مربوطه متفاوت است.

15.6. جلسه [nuxt]

Image

سشن [nuxt] (بین کلاینت و سرور Nuxt) در داخل شیء زیر [session] محصور خواهد شد:


/*eslint-disable no-console */
// تعریف جلسه
const session = {
  // محتوای جلسه
  value: {
    // ذخیره‌سازی مقدار Vuex راه‌اندازی نشده است
    initStoreDone: false,
    //مقدار استور Vuex
    store: ''
  },
  // ذخیره جلسه در کوکی
  save(context) {
    // ذخیره کردن استور در جلسه
    this.value.store = context.store.state
    console.log('nuxt-session save=', this.value)
    // ذخیره مقدار جلسه
    context.app.$cookies.set('nuxt-session', this.value, { path: context.base, maxAge: context.env.maxAge })
  },
  // بازنشانی جلسه
  reset(context) {
    console.log('nuxt-session reset')
    // ریست کردن استور
    context.store.commit('reset')
    //ذخیره فروشگاه جدید در جلسه و ذخیره جلسه
    this.save(context)
  }
}
// صادرات جلسه
export default session
  • خطوط ۵–۱۰: جلسه تنها یک ویژگی دارد، [value]، با دو زیر-ویژگی:
    • [initStoreDone]، که نشان می‌دهد آیا استور inicialize شده است یا خیر؛
    • [store]: مقدار [store.state] از استور Vuex برنامه؛
  • خطوط ۱۲–۱۸: متد [save] برای ذخیره کردن جلسه [nuxt] در یک کوکی استفاده می‌شود. کتابخانه [cookie-universal-nuxt] در اینجا برای مدیریت کوکی استفاده می‌شود. به نام کوکی جلسه [nuxt] توجه کنید: [nuxt-session] (خط 17);
  • خطوط ۲۰–۲۶: متد [reset] جلسه [nuxt] را ریست می‌کند؛
    • خط ۲۳: استور Vuex ریست می‌شود و سپس در خط ۲۵ در جلسه ذخیره می‌شود؛

15.7. پلاگین‌های مدیریت جلسه [nuxt]

Image

15.7.1. پلاگین مدیریت جلسه [nuxt] برای serveur و [nuxt]

وقتی برنامه اجرا می‌شود، سرور [nuxt] اولین سروری است که راه‌اندازی می‌شود. بنابراین این سرور است که جلسه [nuxt] را آغاز می‌کند. اسکریپت [server/plgSession] به شرح زیر است:


/* eslint-disable no-console */

// وارد کردن جلسه
import session from '@/entities/session'

export default (context, inject) => {
  // مدیریت جلسه سرور
  console.log('[plugin server plgSession]')

  //آیا جلسهٔ موجودی وجود دارد؟
  const value = context.app.$cookies.get('nuxt-session')
  if (!value) {
    // جلسه جدید
    console.log("[plugin server plgSession], démarrage d'une nouvelle session")
  } else {
    // جلسه موجود
    console.log("[plugin server plgSession], reprise d'une session existante")
    session.value = value
  }

  // یک تابع به [context, Vue] تزریق می‌شود که جلسه را به جلسهٔ جاری تبدیل می‌کند
  inject('session', () => session)
}
  • خط ۴: کد جلسه برای [nuxt] وارد می‌شود؛
  • خط ۱۱: مقدار کوکی جلسه [nuxt] بازیابی می‌شود؛
  • خطوط ۱۲–۱۵: اگر کوکی جلسه برای [nuxt] وجود نداشت، آنگاه جلسه [nuxt] که در خط ۴ وارد شده است کافی است. کار دیگری برای انجام دادن وجود ندارد؛
  • خطوط ۱۵–۱۹: اگر کوکی جلسه [nuxt] وجود داشت، در خط ۱۸ مقدار آن در جلسه‌ای که در خط ۴ وارد شده است ذخیره می‌شود؛
  • خط ۲۲: جلسه یا آغاز شده یا بازیابی شده است. این جلسه از طریق تابع [$session] در دسترس قرار می‌گیرد؛

15.7.2. پلاگین مدیریت جلسه [nuxt] برای کلاینت [nuxt]

اسکریپت [client/plgSession] به شرح زیر است:


/* eslint-disable no-console */

// وارد کردن جلسه
import session from '@/entities/session'

export default (context, inject) => {
  // مدیریت جلسهٔ کلاینت
  console.log('[plugin client plgSession], reprise de la session [nuxt] du serveur')
  //بازیابی جلسهٔ موجود از سرور Nuxt
  session.value = context.app.$cookies.get('nuxt-session')

  //تزریق یک تابع به [context, Vue] که جلسه را جاری می‌کند
  inject('session', () => session)
}
  • خط ۴: جلسه [nuxt] وارد می‌شود؛
  • خط ۱۰: جلسهٔ جاری [nuxt] از کوکی [nuxt-session] بازیابی می‌شود؛
  • خط ۱۳: جلسه [nuxt] که در خط ۴ وارد شده است، از طریق تابع تزریقی [$session] بازگردانده می‌شود؛

15.8. پلاگین‌ها برای لایه‌های [dao]

Image

15.8.1. پلاگین لایه [dao] برای کلاینت [nuxt]

اسکریپت [client/plgDao] به شرح زیر است:


/*eslint-disable no-console */
//یک نقطه دسترسی به لایه [Dao] ایجاد می‌کند
import Dao from '@/api/client/Dao'
export default (context, inject) => {
  //پیکربندی Axios
  context.$axios.defaults.timeout = context.env.timeout
  context.$axios.defaults.baseURL = context.env.baseURL
  context.$axios.defaults.withCredentials = context.env.withCredentials
  // نمونه سازی لایه [dao]
  const dao = new Dao(context.$axios)
  //تزریق یک تابع [$dao] به زمینه
  inject('dao', () => dao)
  // لاگ
  console.log('[fonction client $dao créée]')
}
  • خط ۳: لایه [dao] از کلاینت [nuxt] وارد می‌شود؛
  • خطوط ۶–۸:شیء [context.$axios] که درخواست‌های HTTP را از لایه [dao] مشتری [nuxt] با استفاده از اطلاعات فایل [nuxt.config] اجرا خواهد کرد:

// محیط
  env: {
    //پیکربندی Axios
    timeout: 2000,
    withCredentials: true,
    baseURL: 'http://'localhost/php7/scripts-web/impots/version-14',
    // تنظیمات کوکی جلسه [nuxt]
    maxAge: 60 * 5
  }
  • خط ۱۰: لایه [dao] از کلاینت [nuxt] نمونه سازی می‌شود؛
  • خط ۱۲: تابع [$dao] به زمینه و صفحات کلاینت تزریق می‌شود. این تابع دسترسی به لایه [dao] از خط ۱۰ را فراهم می‌کند؛

بنابراین باید توجه داشت که برای دسترسی به لایه [dao] کلاینت [nuxt] در حین اجرای آن، باید نوشت:

  • [context.app.$dao()] در جایی که زمینه مشخص است؛
  • [this.$dao()] در داخل یک صفحه [Vue.js

15.8.2. پلاگین برای لایه [dao] از serveur [nuxt]

اسکریپت [server/plgDao] به شرح زیر است:


/* eslint-disable no-console */
//ایجاد یک نقطه دسترسی به لایه [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
  //پیکربندی axios
  context.$axios.defaults.timeout = context.env.timeout
  context.$axios.defaults.baseURL = context.env.baseURL
  // بازیابی کوکی جلسه
  const store = context.app.$session().value.store
  const phpSessionCookie = store ? store.phpSessionCookie : ''
  console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
  // مثال‌سازی لایه [dao]
  const dao = new Dao(context.$axios, phpSessionCookie)
  //تزریق یک تابع [$dao] به زمینه
  inject('dao', () => dao)
  // لاگ
  console.log('[fonction server $dao créée]')
}
  • خط ۳: لایه [dao] از سرور [nuxt] وارد می‌شود؛
  • خطوط ۶–۷:شیء [context.$axios]، که با استفاده از اطلاعات فایل [nuxt.config]، درخواست‌های HTTP را از لایه [dao] سرور [nuxt] ارسال می‌کند:

// محیط
  env: {
    //پیکربندی Axios
    timeout: 2000,
    withCredentials: true,
    baseURL: 'http://'localhost/php7/scripts-web/impots/version-14',
    // تنظیمات کوکی جلسه [nuxt]
    maxAge: 60 * 5
  }
  • خط ۹: مخزن برای برنامه [nuxt] بازیابی می‌شود؛
  • خط ۱۰: اگر فروشگاه وجود داشته باشد، کوکی جلسه برای PHP بازیابی می‌شود، زیرا برای نمونه‌سازی لایه [dao] سرور [nuxt] مورد نیاز است؛
  • خط ۱۳: لایه [dao] از سرور [nuxt] ایجاد می‌شود؛
  • خط ۱۵: تابع [$dao] به زمینه و صفحات سرور [nuxt] تزریق می‌شود. این تابع دسترسی به لایه [dao] از خط ۱۳ را فراهم می‌کند؛

بنابراین باید توجه داشت که برای دسترسی به لایه [dao] سرور [nuxt] در حین اجرا، باید نوشت:

  • [context.app.$dao()] در جایی که زمینه مشخص است؛
  • [this.$dao()] در داخل یک صفحه [Vue.js

15.9. استور Vuex

Image

فروشگاه [Vuex] تمام داده‌هایی را که نیاز است بین اجزای مختلف برنامه [pages, client, serveur] به اشتراک گذاشته شود، ذخیره می‌کند، بدون اینکه این داده‌ها واکنشی باشند.


/* eslint-disable no-console */

//وضعیت شاتر
export const state = () => ({
  // jSON جلسه شروع شد
  jsonSessionStarted: false,
  // کاربر احراز هویت شد
  userAuthenticated: false,
  // cookie جلسه PHP
  phpSessionCookie: '',
  //adminData
  adminData: ''
})

// ذخیره‌سازی به‌روزرسانی‌ها
export const mutations = {
  // تعویض وضعیت
  replace(state, newState) {
    for (const attr in newState) {
      state[attr] = newState[attr]
    }
  },
  // ریست فروشگاه
  reset() {
    this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
  }
}

// اقدامات فروشگاه
export const actions = {
  nuxtServerInit(store, context) {
    // چه کسی این کد را اجرا می‌کند؟
    console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
    // جلسه را راه‌اندازی کنید
    initStore(store, context)
  }
}

function initStore(store, context) {
  // store همان استوری است که باید اولیه شود
  // جلسه را بازیابی کنید
  const session = context.app.$session()
  //آیا جلسه قبلاً راه‌اندازی شده است؟
  if (!session.value.initStoreDone) {
    // شروع یک فروشگاه جدید
    console.log("nuxtServerInit, initialisation d'une nouvelle session")
    //فروشگاه به جلسه اضافه می‌شود
    session.value.store = store.state
    //فروشگاه اکنون راه‌اندازی شده است
    session.value.initStoreDone = true
  } else {
    console.log("nuxtServerInit, reprise d'un store existant")
    // به‌روزرسانی فروشگاه با ذخیره‌سازی جلسه
    store.commit('replace', session.value.store)
  }
  //ذخیرهٔ جلسه
  session.save(context)
  //ثبت
  console.log('initStore terminé, store=', store.state)
}

داده‌های ذخیره‌شده در فروشگاه به شرح زیر است:

  • خط ۶: [jsonSessionStarted] به محض موفقیت‌آمیز بودن راه‌اندازی یک جلسه jSON با سرور PHP، به true تنظیم خواهد شد، چه این کار توسط کلاینت انجام شود یا سرور [nuxt]. با تکمیل این راه‌اندازی اولیه، کوکی جلسه برای سرور PHP بازیابی شده و در ویژگی [phpSessionCookie]، خط ۱۰؛ قرار داده خواهد شد.
  • خط ۸: [userAuthenticated] به محض موفقیت احراز هویت با سرور PHP به true تنظیم خواهد شد، چه این کار توسط کلاینت یا سرور [nuxt] انجام شده باشد؛
  • خط ۱۲: [adminData] برابر با مقدار [adminData] خواهد بود که از سرور PHP پس از موفقیت احراز هویت دریافت شده است؛
  • خطوط ۱۸–۲۲: تخصیص [replace] ویژگی‌های قبلی را با ویژگی‌های شیء ارسال‌شده به‌عنوان پارامتر مقداردهی اولیه می‌کند؛
  • خطوط ۲۴–۲۶: تخصیص [reset] ویژگی‌های فروشگاه را به مقادیر اولیهٔ خود بازمی‌گرداند؛
  • خطوط ۳۱–۳۷: تابع [nuxtServerInit] کار خود را به تابع [initStore] واگذار می‌کند؛
  • خطوط ۳۹–۶۰: تابع [initStore] دو نقش دارد:
    • اگر مخزن هنوز اولیه نشده باشد، اولیه شده و به جلسه اضافه می‌شود؛
    • اگر store قبلاً مقداردهی اولیه شده باشد، مقدار آن از جلسه [nuxt] بازیابی می‌شود؛
  • خط ۴۲: جلسه Nuxt بازیابی می‌شود؛
  • خط ۴۴: بررسی می‌کنیم که آیا استور مقداردهی اولیه شده است:
    • در غیر این صورت، مخزن اولیه در جلسه قرار داده می‌شود (خط ۴۸);
    • سپس، در خط ۵۰، نشان می‌دهیم که استور راه‌اندازی شده است؛
  • خطوط ۵۱–۵۵: اگر فروشگاه راه‌اندازی شده باشد، سپس در خط ۵۴ از آن استفاده می‌کنیم تا فروشگاه را با مقداری که در جلسه وجود دارد راه‌اندازی کنیم؛
  • خط ۵۷: در همه موارد، جلسه به همراه ذخیره‌ای که در خود دارد، در کوکی [nuxt-session] ذخیره می‌شود؛

15.10. پلاگین [plgEventBus]

Image

این افزونه با هدف دسترس‌پذیر ساختن یک اتوبوس رویداد برای کلاینت [nuxt] از طریق یک تابع [$eventBus] که در زمینه کلاینت [nuxt] تزریق شده است، طراحی شده است. تزریق آن به زمینهٔ سرور [nuxt] بی‌فایده است، زیرا سرور قادر به پردازش رویدادها نیست. با این حال، قبلاً دیده‌ایم که تزریق آن در سمت سرور و سپس استفاده از آن خطایی ایجاد نمی‌کند.


/* eslint-disable no-console */
//یک باس رویداد بین ویوها ایجاد می‌کند
import Vue from 'vue'
export default (context, inject) => {
  // اتوبوس رویداد
  const eventBus = new Vue()
  //تزریق یک تابع [$eventBus] به زمینه
  inject('eventBus', () => eventBus)
  // لاگ
  console.log('[fonction $eventBus créée]')
}

ما قبلاً این افزونه را در بخش «link» دیده‌ایم. تابع [$eventBus] از طریق نشانه‌گذاری‌های زیر در دسترس کلاینت قرار خواهد گرفت:

  • [context.app.$eventBus()] در جایی که زمینه در دسترس است؛
  • [this.$eventBus()] در صفحات [Vue.js] سمت کلاینت؛

15.11. اجزای برنامه [nuxt]

Image

کامپوننت [layout] همان کامپوننت مثال‌های قبلی است:


<!--چیدمان نما -->
<template>
  <!-- ردیف -->
  <div>
    <b-row>
      <!-- میدان سه‌ستونی -->
      <b-col v-if="left" cols="3">
        <slot name="left" />
      </b-col>
      <!-- منطقهٔ نه ستونی -->
      <b-col v-if="right" cols="9">
        <slot name="right" />
      </b-col>
    </b-row>
  </div>
</template>

<script>
export default {
  // تنظیمات
  props: {
    left: {
      type: Boolean
    },
    right: {
      type: Boolean
    }
  }
}
</script>

کامپوننت [navigation] به شرح زیر است:


<template>
  <!--منوی بوت‌استرپ سه‌گزینه‌ای -->
  <b-nav vertical>
    <b-nav-item to="/authentification" exact exact-active-class="active">
      Authentification
    </b-nav-item>
    <b-nav-item to="/get-admindata" exact exact-active-class="active">
      Requête AdminData
    </b-nav-item>
    <b-nav-item to="/fin-session" exact exact-active-class="active">
      Fin session impôt
    </b-nav-item>
  </b-nav>
</template>

15.12. چیدمان‌های برنامه [nuxt]

Image

15.12.1. [default]

قالب [default] همان قالبی است که برای مثال [nuxt-11] در بخش «لینک» استفاده می‌شود:


<template>
  <div class="container">
    <b-card>
      <!-- یک پیام -->
      <b-alert show variant="success" align="center">
        <h4>[nuxt-12] : requêtes HTTP avec axios</h4>
      </b-alert>
      <!--نمای مسیریابی فعلی -->
      <nuxt />
      <!-- پیام در حال انتظار -->
      <b-alert v-if="showLoading" show variant="light">
        <strong>Requête au serveur de données en cours...</strong>
        <div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
      </b-alert>
      <!-- خطا در یک عملیات ناهمزمان -->
      <b-alert v-if="showErrorLoading" show variant="danger">
        <strong>La requête au serveur de données a échoué : {{ errorLoadingMessage }}</strong>
      </b-alert>
    </b-card>
  </div>
</template>

<script>
/*eslint-disable no-console */
export default {
  name: 'App',
  data() {
    return {
      showLoading: false,
      showErrorLoading: false
    }
  },
  // چرخهٔ عمر
  beforeCreate() {
    console.log('[default beforeCreate]')
  },
  created() {
    console.log('[default created]')
    if (process.client) {
      // در حال گوش دادن به رویداد [loading]
      this.$eventBus().$on('loading', this.mShowLoading)
      // و همچنین رویداد [errorLoadingMessage]
      this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
    }
  },
  beforeMount() {
    console.log('[default beforeMount]')
  },
  mounted() {
    console.log('[default mounted]')
  },
  methods: {
    // پردازش پیام در انتظار
    mShowLoading(value) {
      console.log('[default mShowLoading], showLoading=', value)
      this.showLoading = value
    },
    //خطا در یک عملیات ناهمزمان
    mShowErrorLoading(value, errorLoadingMessage) {
      console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
      this.showErrorLoading = value
      this.errorLoadingMessage = errorLoadingMessage
    }
  }
}
</script>
  • خطوط ۱۰–۱۴: نمایش پیامی که نشان می‌دهد کلاینت در انتظار تکمیل یک عملیات ناهمزمان است ([nuxt]);
  • خطوط ۱۵–۱۸: نمایش هرگونه پیام خطا مربوط به یک عملیات ناهمزمان؛
  • خط ۳۷: تابع [created] در صفحه [default] قبل از تابع [mounted] در صفحات اجرا می‌شود؛
  • خط ۳۹: اگر اجراکننده مشتری [nuxt] باشد، صفحه [default] منتظر رویدادها می‌ماند:
    • [loading]، که شروع یا پایان یک انتظار را سیگنال می‌دهد. سپس تابع [mShowLoading] اجرا می‌شود؛
    • [errorLoading] که سیگنال می‌دهد یک پیام خطا باید نمایش داده شود. سپس تابع [mShowErrorLoading] اجرا می‌شود؛
  • صفحات [nuxt]:
    • با ارسال رویداد [‘loading’, true] در اتوبوس رویدادها، پیام انتظار را نمایش دهید؛
    • با ارسال رویداد [‘loading’, false] در اتوبوس رویداد، پیام انتظار را پنهان می‌کند؛
    • با ارسال رویداد [‘errorLoading’, true] در اتوبوس رویدادها، یک پیام خطا نمایش دهید؛
    • با ارسال رویداد [‘errorLoading’, false] در اتوبوس رویداد، پیام خطا را پنهان کنید؛

15.12.2. [error]

قالب [error] یک پیام خطای سیستم را نمایش می‌دهد (توسط توسعه‌دهنده مدیریت نمی‌شود):


<!--HTML تعریف نما -->
<template>
  <!-- قالب‌بندی -->
  <Layout :left="true" :right="true">
    <!-- هشدار در ستون سمت راست -->
    <template slot="right">
      <!-- پیام روی پس‌زمینه صورتی -->
      <b-alert show variant="danger" align="center">
        <h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
      </b-alert>
    </template>
    <!-- منوی ناوبری در ستون سمت چپ -->
    <Navigation slot="left" />
  </Layout>
</template>

<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/*eslint-disable nuxt/no-env-in-hooks */

import Layout from '@/components/layout'
import Navigation from '@/components/navigation'

export default {
  name: 'Error',
  // کامپوننت‌های استفاده شده
  components: {
    Layout,
    Navigation
  },
  // ویژگی [props]
  props: { error: { type: Object, default: () => 'waiting ...' } },
  //چرخه عمر
  beforeCreate() {
    // کلاینت و سرور
    console.log('[error beforeCreate]')
  },
  created() {
    // کلاینت و سرور
    console.log('[error created, error=]', this.error)
  },
  beforeMount() {
    //فقط کلاینت
    console.log('[error beforeMount]')
  },
  mounted() {
    // فقط کلاینت
    console.log('[error mounted]')
  }
}
</script>

15.13. صفحه [index] توسط سرور [nuxt] اجرا شد

Image

صفحه [index.vue] منحصر به فرد است زیرا فقط از طریق سرور [nuxt] قابل دسترسی است. هیچ لینکی برای دسترسی کاربر از طریق کلاینت [nuxt] ارائه نشده است. کد آن به شرح زیر است:


<!-- صفحه اصلی -->
<template>
  <Layout :left="true" :right="true">
    <!-- ناوبری -->
    <Navigation slot="left" />
    <!-- پیام-->
    <b-alert slot="right" show variant="warning">Initialisation de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'InitSession',
  // کامپوننت‌های استفاده شده
  components: {
    Layout,
    Navigation
  },
  // داده‌های ناهمزمان
  async asyncData(context) {
    // لاگ
    console.log('[index asyncData started]')
    try {
      // شروع یک جلسه jSON
      const dao = context.app.$dao()
      const response = await dao.initSession()
      // لاگ
      console.log('[index asyncData response=]', response)
      //بازیابی کوکی جلسه PHP برای درخواست‌های بعدی
      const phpSessionCookie = dao.getPhpSessionCookie()
      //کوکی جلسه PHP در جلسه [nuxt] ذخیره می‌شود
      context.store.commit('replace', { phpSessionCookie })
      //آیا خطایی رخ داد؟
      if (response.état !== 700) {
        //خطا در response.réponse است
        throw new Error(response.réponse)
      }
      //توجه داشته باشید که جلسه jSON آغاز شده است
      context.store.commit('replace', { jsonSessionStarted: true })
      //نتیجه بازگردانده می‌شود
      return { result: '[succès]' }
    } catch (e) {
      // لاگ
      console.log('[index asyncData error=]', e)
      // مشخص شد که جلسه jSON هنوز شروع نشده است
      context.store.commit('replace', { jsonSessionStarted: false })
      //خطا گزارش شده است
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      //فروشگاه ذخیره شد
      const session = context.app.$session()
      session.save(context)
      // لاگ
      console.log('[index asyncData finished]')
    }
  },
  //چرخه عمر
  beforeCreate() {
    console.log('[index beforeCreate]')
  },
  created() {
    console.log('[index created]')
  },
  beforeMount() {
    console.log('[index beforeMount]')
  },
  mounted() {
    console.log('[index mounted]')
    // فقط مشتری
    if (this.showErrorLoading) {
      console.log('[index mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>
  • خط ۷: صفحه نتیجه [result] یک درخواست غیرهمزمان (خطوط ۴۶ و ۵۱) را نمایش می‌دهد؛
  • خط ۳۱: عملیات غیرهمزمان، باز کردن یک جلسه jSON با سرور محاسبه مالیات است؛
  • خط 25: می‌دانیم که وقتی صفحه مستقیماً از سرور [nuxt] درخواست می‌شود، توابع [asyncData] فقط توسط سرور اجرا می‌شوند و نه توسط کلاینت [nuxt]، که پس از دریافت پاسخ از سرور [nuxt] اجرا می‌شود؛
  • خط ۳۰: لایه [dao] از زمینه سرور [nuxt] بازیابی می‌شود؛
  • خط ۳۵: اگر سرور هنوز درخواستی به سرور محاسبه مالیات ارسال نکرده باشد، اولین کوکی جلسه خود PHP را دریافت می‌کند؛ در غیر این صورت، آخرین کوکی جلسه PHP را که دریافت کرده بود، دریافت می‌کند (به کد لایه [dao] از سرور [nuxt] در بخش «لینک» مراجعه کنید)؛
  • خط ۳۷: این کوکی جلسه PHP در فروشگاه ذخیره می‌شود؛
  • خطوط ۳۹–۴۲: بررسی می‌کنیم که آیا عملیات موفقیت‌آمیز بوده است یا خیر. در صورت عدم موفقیت، یک استثنا پرتاب می‌شود که در خط ۴۷ توسط [catch] گرفته خواهد شد؛
  • خط ۴۴: ما در فروشگاه ثبت می‌کنیم که جلسه jSON با سرور PHP آغاز شده است؛
  • خط ۴۶: نتیجه [result] بازگردانده شده و در خط ۷ نمایش داده می‌شود؛
  • خطوط ۴۷–۵۴: هرگونه استثنا رسیدگی می‌شود. این موارد ممکن است از دو نوع باشند:
    • عملیات HTTP در خط ۳۱ به دلیل خطای ارتباطی بین سرور [nuxt] و سرور PHP ناموفق بود؛
    • عملیات HTTP در خط ۳۱ با موفقیت انجام شد اما نتیجه دریافتی نشان‌دهنده خطا بود (خطوط ۳۹–۴۲)؛
  • خط ۵۱: توجه داشته باشید که جلسه jSON با سرور PHP شروع نشد؛
  • خط ۵۳: نتیجه [result] بازگردانده شده و در خط ۷ نمایش داده می‌شود. علاوه بر این، ویژگی‌های [showErrorLoading] و [errorLoadingMessage] تنظیم شده‌اند؛ کلاینت [nuxt] از این ویژگی‌ها برای نمایش یک پیام خطا هنگام دریافت صفحه‌ای که توسط سرور [nuxt] ارسال شده است، استفاده خواهد کرد. (خطوط ۷۲–۷۹)؛
  • خطوط 54–60: کد در همه موارد (موفقیت یا شکست) اجرا می‌شود؛
  • خط ۵۶: جلسه [nuxt] از زمینه سرور [nuxt] بازیابی می‌شود؛
  • خط ۵۷: ذخیره می‌شود؛
  • خطوط ۶۳–۶۸: پس از اتمام تابع [asyncData]، سرور [nuxt] توابع [beforeCreate] و [create] را اجرا می‌کند؛

توجه: اجرای صفحه [index] توسط سرور [nuxt] ممکن است با شکست مواجه شود، برای مثال، اگر سرور محاسبه مالیات هنگام راه‌اندازی برنامه [nuxt] در حال اجرا نباشد:

Image

در این صورت، تنها راه‌حل این است که ابتدا سرور محاسبه مالیات را راه‌اندازی کرده و سپس خود برنامه [nuxt] را اجرا کنید، زیرا منوی ناوبری گزینه‌ای برای شروع یک جلسه jSON با سرور محاسبه مالیات ارائه نمی‌دهد؛

15.14. صفحه [index] که توسط کلاینت [nuxt] اجرا می‌شود

صفحه [index] تنها توسط کلاینت [nuxt] اجرا می‌شود، پس از آنکه سرور [nuxt] آن را برای کلاینت ارسال کرده باشد. سرور اطلاعات [result] و در صورت لزوم [showErrorLoading] و [errorLoadingMessage] را برای آن ارسال کرده است.

ما می‌دانیم که تابع [asyncData] اجرا نخواهد شد. این موضوع ما را به توابع چرخهٔ عمر می‌رساند، به‌ویژه تابع [mounted]:


mounted() {
    console.log('[index mounted]')
    //فقط مشتری
    if (this.showErrorLoading) {
      console.log('[index mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
}
  • کلاینت [nuxt] به‌طور خودکار عناصر [result] و در صورت لزوم [showErrorLoading, errorLoadingMessage] را که توسط سرور [nuxt] ارسال شده‌اند، در ویژگی‌های صفحه ادغام می‌کند:
  • ویژگی [result] در خط ۷ نمایش داده می‌شود؛
  • ویژگی‌های [showErrorLoading, errorLoadingMessage] توسط متد [mounted] استفاده می‌شوند: در خط ۴، ویژگی [showErrorLoading] بررسی می‌شود. اگر این مقدار true باشد، در خط 6، از باس رویدادهای کلاینت [nuxt] برای سیگنال‌دهی به وجود یک پیام خطا برای نمایش استفاده می‌شود؛
  • رویداد [errorLoading] که در خط 6 فعال می‌شود، توسط صفحه‌ی [layouts/default] که در بخش «لینک» توصیف شده است، رهگیری می‌شود؛

15.15. صفحه [authentification] توسط سرور [nuxt] اجرا می‌شود

صفحه [authentification] مسئول احراز هویت کاربر با سرور محاسبه مالیات است. کد آن به شرح زیر است:


<!--صفحهٔ احراز هویت -->
<template>
  <Layout :left="true" :right="true">
    <!-- ناوبری -->
    <Navigation slot="left" />
    <!-- پیام-->
    <b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Authentification',
  // کامپوننت‌های استفاده شده
  components: {
    Layout,
    Navigation
  },
  // داده‌های ناهمزمان
  async asyncData(context) {
    // لاگ
    console.log('[authentification asyncData started]')
    if (process.client) {
      // انتظار سمت کلاینت آغاز می‌شود [nuxt]
      context.app.$eventBus().$emit('loading', true)
      // بدون خطا
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // احراز هویت با سرور
      const dao = context.app.$dao()
      const response = await dao.authentifierUtilisateur('admin', 'admin')
      // لاگ
      console.log('[authentification asyncData response=]', response)
      // نتیجه
      const userAuthenticated = response.état === 200
      // توجه کنید که آیا کاربر احراز هویت شده است یا خیر
      context.store.commit('replace', { userAuthenticated })
      //فروشگاه در جلسه ذخیره می‌شود [nuxt]
      const session = context.app.$session()
      session.save(context)
      // خطای احراز هویت؟
      if (!userAuthenticated) {
        //خطا در response.réponse است
        throw new Error(response.réponse)
      }
      //نتیجه بازگردانده می‌شود
      return { result: '[succès]' }
    } catch (e) {
      //خطا گزارش شده است
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // لاگ
      console.log('[authentification asyncData finished]')
      if (process.client) {
        //پایان انتظار مشتری [nuxt]
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  //چرخه عمر
  beforeCreate() {
    console.log('[authentification beforeCreate]')
  },
  created() {
    console.log('[authentification created]')
  },
  beforeMount() {
    console.log('[authentification beforeMount]')
  },
  mounted() {
    console.log('[authentification mounted]')
    // فقط مشتری
    if (this.showErrorLoading) {
      console.log('[authentification mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>
  • خط ۷: صفحه نتیجه [result] درخواست ناهمزمان [asyncData] از خطوط ۲۵–۶۵ را نمایش می‌دهد؛
  • خطوط ۲۸–۳۳: سرور این خطوط را که برای کلاینت [nuxt] در نظر گرفته شده‌اند، اجرا نمی‌کند؛
  • خط ۳۶: لایه [dao] از سرور [nuxt] بازیابی می‌شود؛
  • خط ۳۷: ما با استفاده از اعتبارنامه‌های آزمایشی [admin, admin] که تنها اعتبارنامه‌های پذیرفته‌شده توسط سرور محاسبه مالیات هستند، احراز هویت می‌کنیم؛
  • خط ۴۱: عملیات احراز هویت تنها در صورتی موفقیت‌آمیز است که وضعیت پاسخ ۲۰۰ باشد؛
  • خط ۴۳: ویژگی [userAuthenticated] در فروشگاه ذخیره می‌شود؛
  • خطوط ۴۴–۴۶: فروشگاه در جلسه [nuxt] ذخیره می‌شود؛
  • خطوط ۴۸–۵۱: اگر احراز هویت ناموفق باشد، یک استثنا با پیام خطای ارسال‌شده توسط سرور محاسبه مالیات پرتاب می‌شود؛
  • در غیر این صورت، در خط ۵۳، یک نتیجه موفقیت بازگردانده می‌شود که در خط ۷ نمایش داده خواهد شد؛
  • خطوط ۵۴–۵۷: در صورت بروز خطا، سه ویژگی از صفحه [result, showErrorLoading, errorLoadingMessage] مقداردهی می‌شوند. ویژگی [result] در خط ۷ نمایش داده خواهد شد. این سه ویژگی به کلاینت [nuxt] ارسال خواهند شد؛
  • خطوط ۶۰–۶۳: توسط سرور [nuxt] اجرا نمی‌شوند؛
  • پس از اینکه [asyncData] نتیجه خود را بازگرداند، این نتیجه در خط ۷ نمایش داده می‌شود. سپس متدهای [beforeCreate] (خطوط ۶۷–۶۹) و [created] (خطوط ۷۰–۷۲) اجرا می‌شوند؛
  • همین؛

توجه: اجرای صفحه [authentification] توسط سرور [nuxt] ممکن است با شکست مواجه شود، برای مثال اگر جلسه jSON با سرور محاسبه مالیات راه‌اندازی نشده باشد. این کار را می‌توان به شرح زیر انجام داد:

  • کوکی جلسه PHP را از مرورگر خود حذف کنید (برای شروع مجدد):

Image

  • برنامه [nuxt] را در حالی که سرور محاسبه مالیات راه‌اندازی نشده است، اجرا کنید: یک خطا دریافت خواهید کرد؛
  • سرور محاسبه مالیات را راه‌اندازی کنید؛
  • آدرس URL [/authentification] را مستقیماً در نوار آدرس مرورگر وارد کنید:

Image

در این صورت، تنها راه‌حل بار دیگر بارگذاری مجدد صفحه [index] است.

15.16. صفحه [authentification] توسط کلاینت [nuxt] اجرا شد

بیایید دوباره کد صفحه را بررسی کنیم:


<!-- صفحه احراز هویت -->
<template>
  <Layout :left="true" :right="true">
    <!-- ناوبری -->
    <Navigation slot="left" />
    <!-- پیام-->
    <b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Authentification',
  // کامپوننت‌های استفاده شده
  components: {
    Layout,
    Navigation
  },
  // داده‌های ناهمزمان
  async asyncData(context) {
    // لاگ
    console.log('[authentification asyncData started]')
    if (process.client) {
      // آغاز انتظار کلاینت [nuxt]
      context.app.$eventBus().$emit('loading', true)
      // بدون خطا
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // احراز هویت با سرور
      const dao = context.app.$dao()
      const response = await dao.authentifierUtilisateur('admin', 'admin')
      // لاگ
      console.log('[authentification asyncData response=]', response)
      // نتیجه
      const userAuthenticated = response.état === 200
      // توجه کنید که آیا کاربر احراز هویت شده است یا خیر
      context.store.commit('replace', { userAuthenticated })
      //فروشگاه در جلسه ذخیره می‌شود [nuxt]
      const session = context.app.$session()
      session.save(context)
      // خطای احراز هویت؟
      if (!userAuthenticated) {
        //خطا در response.réponse است
        throw new Error(response.réponse)
      }
      // نتیجه بازگردانده می‌شود
      return { result: '[succès]' }
    } catch (e) {
      //خطا گزارش شده است
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // لاگ
      console.log('[authentification asyncData finished]')
      if (process.client) {
        //پایان انتظار مشتری [nuxt]
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  //چرخه عمر
  beforeCreate() {
    console.log('[authentification beforeCreate]')
  },
  created() {
    console.log('[authentification created]')
  },
  beforeMount() {
    console.log('[authentification beforeMount]')
  },
  mounted() {
    console.log('[authentification mounted]')
    //فقط برای مشتری
    if (this.showErrorLoading) {
      console.log('[authentification mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>

دو سناریو وجود دارد که در آن‌ها صفحه [authentification] توسط کلاینت [nuxt] اجرا می‌شود:

  1. کلاینت [nuxt] پس از آنکه سرور [nuxt] صفحه [authentification] را به مرورگر کلاینت [nuxt] ارسال کرد، اجرا می‌شود؛
  2. کلاینت [nuxt] به این دلیل که کاربر روی لینک [Authentification] در منوی ناوبری کلیک کرده است:

Image

ابتدا مورد اول را بررسی کنیم. در این مورد، کلاینت [nuxt] تابع [asyncData] را اجرا نمی‌کند. این شامل عناصر [result] و در صورت لزوم [showErrorLoading, errorLoadingMessage]—که توسط سرور [nuxt] برای آن ارسال شده‌اند—در ویژگی‌های صفحه می‌شود:

  • ویژگی [result] توسط خط ۷ نمایش داده می‌شود؛
  • ویژگی‌های [showErrorLoading, errorLoadingMessage] توسط متد [mounted] استفاده می‌شوند: در خط 79، ویژگی [showErrorLoading] بررسی می‌شود. اگر این مقدار true باشد، در خط ۸۱، از باس رویداد کلاینت [nuxt] برای ارسال سیگنال مبنی بر وجود یک پیام خطا برای نمایش استفاده می‌شود؛

مکانیزم نمایش پیام خطا برای صفحه [index] در بخش «لینک» توضیح داده شده است.

مورد دوم مربوط به کلاینت [nuxt] است که هنگام کلیک کاربر روی لینک [Authentification] اجرا می‌شود. در این حالت، کلاینت [nuxt] به‌طور مستقل اجرا می‌شود و نه پس از سرور [nuxt]. سپس تابع [asyncData] اجرا می‌شود. ما تنها جزئیاتی را ارائه می‌دهیم که با توضیحات ارائه‌شده برای صفحه‌ای که توسط سرور [nuxt] اجرا می‌شود، متفاوت است:

  • خطوط ۲۸–۳۳: کلاینت [nuxt] درخواست می‌کند که پیام بارگذاری نمایش داده شود و هرگونه پیام خطایی که ممکن است قبلاً نمایش داده شده باشد، پاک شود؛
  • خط ۳۶: اکنون لایه [dao] از کلاینت [nuxt] است که در اینجا به دست می‌آید؛
  • خطوط ۶۰–۶۳: کلاینت [nuxt] درخواست می‌کند که پیام بارگذاری پنهان شود؛
  • پس از اتمام کار [asyncData]، چرخه عمر صفحه ادامه خواهد یافت. تابع [mounted] در خطوط ۷۶–۸۳ اجرا خواهد شد. اگر خطایی رخ داده باشد، پیام خطا نمایش داده خواهد شد؛

توجه: برای ایجاد خطا، رویه توضیح‌داده‌شده برای سرور [nuxt] را در انتهای پاراگراف «لینک» دنبال کنید، اما به جای درخواست صفحه [authentification] با تایپ کردن URL آن در نوار آدرس، از لینک [Authentification] در منوی ناوبری استفاده کنید. این کار سپس کلاینت [nuxt] را راه‌اندازی می‌کند.

15.17. صفحه [get-admindata]

کد صفحه [get-admindata] به شرح زیر است:


<!--نمای دریافت داده‌های مدیریت -->
<template>
  <Layout :left="true" :right="true">
    <!-- ناوبری -->
    <Navigation slot="left" />
    <!-- پیام -->
    <b-alert slot="right" show variant="secondary"> Demande de [adminData] au serveur de calcul de l'impôt : {{ result }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'GetAdmindata',
  // کامپوننت‌های استفاده شده
  components: {
    Layout,
    Navigation
  },
  // داده‌های ناهمزمان
  async asyncData(context) {
    // لاگ
    console.log('[get-admindata asyncData started]')
    if (process.client) {
      // شروع انتظار
      context.app.$eventBus().$emit('loading', true)
      // بدون خطا
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // داده‌های درخواستی [admindata]
      const response = await context.app.$dao().getAdminData()
      // لاگ
      console.log('[get-admindata asyncData response=]', response)
      // نتیجه
      const adminData = response.état === 1000 ? response.réponse : ''
      //داده‌ها در مخزن قرار می‌گیرند
      context.store.commit('replace', { adminData })
      // فروشگاه در جلسه ذخیره می‌شود [nuxt]
      const session = context.app.$session()
      session.save(context)
      //آیا خطایی رخ داده است؟
      if (!adminData) {
        //خطا در response.réponse است
        throw new Error(response.réponse)
      }
      //مقدار دریافتی بازگردانده می‌شود
      return { result: adminData }
    } catch (e) {
      // خطا گزارش شده است
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // لاگ
      console.log('[get-admindata asyncData finished]')
      if (process.client) {
        //پایان انتظار
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  //چرخه عمر
  beforeCreate() {
    console.log('[get-admindata beforeCreate]')
  },
  created() {
    console.log('[get-admindata created]')
  },
  beforeMount() {
    console.log('[get-admindata beforeMount]')
  },
  mounted() {
    console.log('[get-admindata mounted]')
    // کلاینت
    if (this.showErrorLoading) {
      console.log('[get-admindata mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>

این صفحه بسیار شبیه به صفحه [authentification] است. توضیحات یکسان هستند، چه توسط سرور [nuxt] و چه توسط کلاینت [nuxt] اجرا شود. با این حال، توجه داشته باشید که خط ۷ دیگر «موفقیت» یا «شکست» را مانند قبل نمایش نمی‌دهد، بلکه مقدار داده‌های دریافتی از سرور محاسبه مالیات (خط ۵۲) را نمایش می‌دهد:

Image

نتیجهٔ بالا با استفاده از هم سرور و هم کلاینت [nuxt] به‌دست آمده است. برای ایجاد خطا، صفحهٔ [get-admindata] را از طریق سرور یا کلاینت [nuxt] بدون احراز هویت درخواست کنید:

Image

15.18. صفحه [fin-session]

کد صفحه به شرح زیر است:


<!--صفحهٔ اصلی -->
<template>
  <Layout :left="true" :right="true">
    <!-- ناوبری -->
    <Navigation slot="left" />
    <!-- پیام-->
    <b-alert slot="right" show variant="warning">Fin de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'FinSession',
  // کامپوننت‌های استفاده شده
  components: {
    Layout,
    Navigation
  },
  // داده‌های ناهمزمان
  async asyncData(context) {
    // لاگ
    console.log('[fin-session asyncData started]')
    // مورد مشتری [nuxt]
    if (process.client) {
      // شروع انتظار
      context.app.$eventBus().$emit('loading', true)
      // بدون خطا
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      //یک جلسه جدید PHP از سرور محاسبه مالیات درخواست شده است
      const dao = context.app.$dao()
      const response = await dao.finSession()
      // لاگ
      console.log('[fin-session asyncData response=]', response)
      //آیا خطایی وجود داشت؟
      if (response.état !== 400) {
        //خطا در response.réponse است
        throw new Error(response.réponse)
      }
      // سرور یک کوکی جلسه جدید ارسال کرده است PHP
      // این کوکی هم توسط سرور و هم توسط کلاینت Nuxt بازیابی می‌شود
      //اگر این کد توسط کلاینت [nuxt] اجرا شود، کوکی جلسه PHP باید به جلسه Nuxt اضافه شود
      // تا افزونه [plgDao] روی سرور [nuxt] بتواند آن را بازیابی کرده و لایه [dao] را با
      //. اگر این کد توسط سرور [nuxt] اجرا شود، کوکی جلسه PHP باید در جلسه Nuxt تنظیم شود
      // تا مسیریابی برای کلاینت [nuxt] بتواند آن را بازیابی کرده و به مرورگر منتقل کند
      const phpSessionCookie = dao.getPhpSessionCookie()
      // ما در فروشگاه ثبت می‌کنیم که جلسه jSON آغاز شده است و کوکی جلسه PHP را ذخیره می‌کنیم
      context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
      //فروشگاه در جلسه [nuxt] ذخیره می‌شود
      const session = context.app.$session()
      session.save(context)
      // نتیجه بازگردانده می‌شود
      return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
    } catch (e) {
      //ثبت
      console.log('[fin-session asyncData error=]', e)
      // گزارش خطا
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // لاگ
      console.log('[fin-session asyncData finished]')
      if (process.client) {
        //پایان انتظار
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  //چرخه عمر
  beforeCreate() {
    console.log('[fin-session beforeCreate]')
  },
  created() {
    console.log('[fin-session created]')
  },
  beforeMount() {
    console.log('[fin-session beforeMount]')
  },
  mounted() {
    console.log('[fin-session mounted]')
    // فقط مشتری
    if (this.showErrorLoading) {
      console.log('[fin-session mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>

این کد بسیار شبیه به کد صفحات قبلی است و توضیحات نیز یکسان هستند. تنها یک نکته وجود دارد که باید به آن توجه کرد: عملیات غیرهمزمان در خط ۳۸ به این معنی است که سرور محاسبه مالیات یک کوکی جلسه جدید به نام PHP ارسال خواهد کرد. توضیحات مربوط به نحوهٔ کار با این کوکی بسته به اینکه این کد توسط سرور ([nuxt]) یا کلاینت اجرا شود، متفاوت است.

بیایید با سرور [nuxt] شروع کنیم:

  • خط ۳۷: لایه [dao] از سرور [nuxt] است که ایجاد می‌شود. بیایید کد سازنده آن را به یاد بیاوریم:

// سازنده
  constructor(axios, phpSessionCookie) {
    // کتابخانه axios
    this.axios = axios
    // ارزش کوکی جلسه
    this.phpSessionCookie = phpSessionCookie
    //نام کوکی جلسه سرور PHP
    this.phpSessionCookieName = 'PHPSESSID'
}

همانطور که در خط ۱ می‌بینیم، سازنده به کوکی جلسه جاری PHP نیاز دارد – جدیدترین کوکی دریافت‌شده، چه از سمت سرور و چه از سمت کلاینت [nuxt

  • خط ۵۲: سرور [nuxt] کوکی را برای جلسه جدید، PHP، یا کوکی قدیمی در صورتی که عملیات پایان جلسه با شکست مواجه شده باشد، بازیابی می‌کند؛
  • خط ۵۴: کوکی جلسه PHP در فروشگاه قرار گرفته و سپس در جلسه [nuxt] در خطوط ۵۶–۵۷ ذخیره می‌شود؛
  • پس از سرور، کلاینت [nuxt] است که صفحه [fin-session] را با داده‌های ارسال‌شده توسط سرور اجرا می‌کند. می‌دانیم که این صفحه، تابع [asyncData] را اجرا نخواهد کرد؛
  • در نهایت، هنگامی که سرور و کلاینت [nuxt] وظایف خود را تکمیل کردند، می‌دانیم که کوکی PHP، که برای ارتباط با سرور محاسبه مالیات مورد نیاز است، در جلسه [nuxt] قرار دارد؛

اینکه کوکی PHP در جلسه [nuxt] قرار دارد برای سرور کافی است، زیرا لایه آن [dao] از آنجا آن را بازیابی خواهد کرد. در پلاگین [server/plgDao] که لایه [dao] سرور را راه‌اندازی می‌کند، موارد زیر نوشته شده است:


/* eslint-disable no-console */
// ایجاد یک نقطه دسترسی به لایه [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
  //پیکربندی Axios
  context.$axios.defaults.timeout = context.env.timeout
  context.$axios.defaults.baseURL = context.env.baseURL
  // بازیابی کوکی جلسه
  const store = context.app.$session().value.store
  const phpSessionCookie = store ? store.phpSessionCookie : ''
  console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
  // آشکارسازی لایه [dao]
  const dao = new Dao(context.$axios, phpSessionCookie)
  //تزریق تابع [$dao] به زمینه
  inject('dao', () => dao)
  // لاگ
  console.log('[fonction server $dao créée]')
}
  • در خط ۱۳، لایه [dao] سرور با استفاده از کوکی جلسه PHP که از جلسه [nuxt] در خطوط ۹–۱۰ گرفته شده است، نمونه سازی می‌شود؛

برای کلاینت [nuxt]، داستان متفاوت است. کوکی را خود کلاینت ارسال نمی‌کند، بلکه مرورگری که روی آن اجرا شده است این کار را انجام می‌دهد. با این حال، این مرورگر از کوکی مربوط به جلسه جدید PHP که توسط سرور [nuxt] دریافت شده است، مطلع نیست. اگر از لینک‌های منوی ناوبری [3] استفاده کنیم:

Image

سرور محاسبه مالیات یک کوکی جلسه منسوخ، PHP، را از مرورگر دریافت کرده و پاسخ خواهد داد که هیچ جلسه‌ای، jSON، با این کوکی مرتبط نیست. ما باید راهی پیدا کنیم تا کوکی جلسه جدید، PHP، را به مرورگر منتقل کنیم.

ما می‌توانیم از میان‌افزار مسیریابی برای انجام این کار استفاده کنیم:

Image

اسکریپت [client/routing]، میان‌افزار مسیریابی است که در فایل [nuxt.config] اعلام شده است:


// روتر
  router: {
    //ریشهٔ برنامه URL
    base: '/nuxt-12/',
    // میان‌افزار مسیریابی
    middleware: ['routing']
},

اسکریپت [middleware/routing] به شرح زیر است:


/* eslint-disable no-console */

//وارد کردن میان‌افزار کلاینت
import clientRouting from './client/routing'

export default function(context) {
  //چه کسی این کد را اجرا می‌کند؟
  console.log('[middleware], process.server', process.server, ', process.client=', process.client)
  if (process.client) {
    //مسیریابی سمت کلاینت
    clientRouting(context)
  }
}
  • خطوط ۹–۱۲: تنها کلاینت با استفاده از تابعی که در خط ۴ وارد شده است مسیریابی می‌شود؛

اسکریپت [middleware/client/routing] به شرح زیر است:


/* eslint-disable no-console */
export default function(context) {
  // چه کسی این کد را اجرا می‌کند؟
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // پردازش کوکی جلسه PHP در مرورگر
  //کوکی جلسه مرورگر PHP باید دقیقاً مشابه کوکی یافت‌شده در جلسه Nuxt باشد
  // عمل [fin-session] یک کوکی جدید PHP دریافت می‌کند (هم سرور و هم کلاینت Nuxt)
  //اگر سرور آن را دریافت کند، کلاینت باید آن را به مرورگر منتقل کند
  //برای ارتباطات خود با سرور PHP
  //این یک سناریوی مسیریابی سمت کلاینت است

  //کوکی جلسه بازیابی می‌شود: PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // اگر وجود داشته باشد، کوکی جلسه PHP به مرورگر اختصاص داده می‌شود
    document.cookie = phpSessionCookie
  }
}

بیایید به وضعیت بلافاصله پس از اجرای صفحه [fin-session] توسط سرور [nuxt] بازگردیم:

Image

اگر روی یکی از لینک‌های منوی [3] کلیک کنید، کلاینت [nuxt] کنترل را به دست می‌گیرد. از آنجا که تغییر صفحه رخ خواهد داد، اسکریپت مسیریابی کلاینت اجرا می‌شود:

  • خط ۱۳: کوکی جلسه PHP در فروشگاه برنامه [nuxt] یافت می‌شود؛
  • خط ۱۴: اگر خالی نباشد، به مرورگر ارسال می‌شود (خط ۱۶). از این نقطه به بعد، مرورگر کلاینت ([nuxt]) کوکی جلسه صحیح (PHP) را در اختیار دارد؛

اسکریپت [client/routing] هر بار که کلاینت [nuxt] صفحات را تغییر می‌دهد اجرا می‌شود. کد اسکریپت صرف‌نظر از صفحه مقصد معتبر است: به زبان ساده، در بیشتر مواقع یک کوکی جلسه PHP را که مرورگر از قبل دارد به آن می‌دهد، به جز در دو مورد:

  • بلافاصله پس از شروع برنامه، سرور [nuxt] صفحه [index] را اجرا می‌کند و یک کوکی جلسه اول PHP دریافت می‌کند که مرورگر کلاینت [nuxt] آن را ندارد؛
  • وقتی سرور [nuxt] صفحه [fin-session] را همانطور که توضیح داده شد اجرا می‌کند؛

حال بیایید موردی را در نظر بگیریم که صفحه [fin-session] صرفاً توسط کلاینت [nuxt] اجرا می‌شود، زیرا لینک آن در منوی ناوبری کلیک شده است. اکنون کلاینت [nuxt] است که تابع [asyncData] را اجرا می‌کند:


try {
      //ما یک جلسه جدید PHP را از سرور محاسبه مالیات درخواست می‌کنیم
      const dao = context.app.$dao()
      const response = await dao.finSession()
      // لاگ
      console.log('[fin-session asyncData response=]', response)
      //آیا خطایی رخ داده است؟
      if (response.état !== 400) {
        //خطا در response.réponse است
        throw new Error(response.réponse)
      }
      // سرور یک کوکی جلسه جدید ارسال کرده است PHP
      // این کوکی هم توسط سرور و هم توسط کلاینت Nuxt بازیابی می‌شود
      //اگر این کد توسط کلاینت [nuxt] اجرا شود، کوکی جلسه PHP باید به جلسه Nuxt اضافه شود
      // تا افزونه [plgDao] روی سرور [nuxt] بتواند آن را بازیابی کرده و لایه [dao] را با
      //. اگر این کد توسط سرور [nuxt] اجرا شود، کوکی جلسه PHP باید در جلسه Nuxt تنظیم شود
      // تا مسیریابی برای کلاینت [nuxt] بتواند آن را بازیابی کرده و به مرورگر منتقل کند
      const phpSessionCookie = dao.getPhpSessionCookie()
      //ما در فروشگاه ثبت می‌کنیم که جلسه jSON آغاز شده است و کوکی جلسه PHP را ذخیره می‌کنیم
      context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
      //فروشگاه در جلسه [nuxt] ذخیره می‌شود
      const session = context.app.$session()
      session.save(context)
      // نتیجه بازگردانده می‌شود
      return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
    } catch (e) {
      // لاگ
      console.log('[fin-session asyncData error=]', e)
      // گزارش خطا
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // لاگ
      console.log('[fin-session asyncData finished]')
      if (process.client) {
        // پایان انتظار
        context.app.$eventBus().$emit('loading', false)
      }
    }
  • خط ۳: این لایه [dao] از کلاینت [nuxt] است که در اینجا به دست آمده است؛
  • خط ۱۸: کوکی جلسه PHP که توسط لایه [dao] از کلاینت [nuxt] بازیابی شده است، ذخیره شده و در فروشگاه قرار می‌گیرد (خط ۲۰) و سپس در جلسه [nuxt] ذخیره می‌شود (خطوط ۲۲–۲۳)؛
  • از این نقطه به بعد، همه چیز به درستی کار می‌کند، زیرا می‌دانیم که لایه [dao] سرور [nuxt] کوکی جلسه PHP را از جلسه [nuxt] بازیابی خواهد کرد؛

15.19. Exécution

برای اجرای این مثال، باید قبل از اجرا اطمینان حاصل کنید که کوکی جلسه [nuxt] و کوکی PHP را از مرورگری که کلاینت [nuxt] را اجرا می‌کند، حذف کنید تا کار را از صفر شروع کنید. در زیر مثالی با استفاده از مرورگر کروم آورده شده است:

Image

15.20. Conclusion

این مثال به‌ویژه پیچیده بود. این مثال دانش حاصل از مثال‌های قبلی را با هم ترکیب کرد: پایداری ذخیره‌سازی در یک جلسه [nuxt]، افزونه‌های تزریق توابع، میان‌افزار مسیریابی، و مدیریت خطا برای عملیات غیرهمزمان. پیچیدگی با این واقعیت که ما می‌خواستیم کاربران بتوانند هم از لینک‌های منوی ناوبری استفاده کنند و هم آدرس‌های URL را به صورت دستی وارد کنند، بدون اینکه برنامه از کار بیفتد، بیشتر افزایش یافت. برای دستیابی به این هدف، ما باید بررسی می‌کردیم که هر صفحه بسته به اینکه توسط کلاینت یا سرور [nuxt] اجرا می‌شد، چگونه رفتار می‌کرد.

این یکنواختی در رفتار بین کلاینت و سرور ضروری نیست. سناریوی رایجی را در نظر بگیرید که:

  • صفحهٔ اول توسط سرور [nuxt] ارائه می‌شود؛
  • تمام صفحات بعدی توسط کلاینت [nuxt] که سپس در حالت [SPA] عمل می‌کند، ارائه می‌شوند؛

با این حال، حتی در این حالت نیز، لازم است خروجی را زمانی که تمام صفحات توسط سرور [nuxt] ارائه می‌شوند، بررسی کرد، زیرا این همان چیزی است که موتورهای جستجو هنگام درخواست آنها دریافت خواهند کرد.