15. مثال [nuxt-12]: طلبات HTTP باستخدام axios
15.1. مقدمة
في هذا المثال الجديد، سنستكشف كيفية إجراء طلبات HTTP باستخدام مكتبة [axios] داخل دوال [asyncData]. بالإضافة إلى ذلك، سنطبق المفاهيم التي تمت تغطيتها بالفعل:
- استخدام المكونات الإضافية من المثال [nuxt-06]:
- تخزين المخزن في ملف تعريف ارتباط الجلسة من المثال [nuxt-06]؛
- التحكم في التنقل باستخدام البرامج الوسيطة من مثال [nuxt-09]؛
- معالجة الأخطاء من مثال [nuxt-11]؛
ستكون بنية المثال كما يلي:

- سيتم استضافة تطبيق [nuxt] على خادم [node.js] [3]، ويتم تنزيله بواسطة المتصفح [1]، الذي سيقوم بعد ذلك بتنفيذه؛
- سيقوم كل من عميل [nuxt] [1] وخادم [nuxt] [3] بإرسال طلبات HTTP إلى خادم البيانات [2]. سيكون هذا الخادم هو خادم حساب الضرائب الذي تم تطويره في قسم PHP 7. سنستخدم أحدث إصدار منه، الإصدار 14، مع تمكين طلبات CORS؛
يمكن تبسيط بنية المثال على النحو التالي:

- في [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. هيكل دليل المشروع

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: [
// Doc: 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) { }
},
// source code directory
srcDir: 'nuxt-12',
// router
router: {
// application URL root
base: '/nuxt-12/',
// routing middleware
middleware: ['routing']
},
// server
server: {
// service port, default 3000
port: 81,
// network addresses listened to, default localhost: 127.0.0.1
// 0.0.0.0 = all the machine's network addresses
host: 'localhost'
},
// environment
env: {
// axios configuration
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// session cookie configuration [nuxt]
maxAge: 60 * 5
}
}
- السطر 22: نتولى بأنفسنا معالجة الإشعار بانتهاء إجراء غير متزامن؛
- السطر 31: سنستخدم العديد من المكونات الإضافية المتخصصة إما للعميل أو للخادم، ولكن ليس لكليهما في نفس الوقت؛
- السطر 52: تم دمج وحدة [axios] في [nuxt]. ونتيجة لذلك، سيكون كائن [axios]، الذي سيتولى معالجة طلبات HTTP من تطبيق [nuxt] إلى خادم حساب الضرائب PHP، متاحًا في [context.$axios]؛
- السطر 54: ستسمح لنا وحدة [cookie-universal-nuxt] بحفظ جلسة [nuxt] في ملف تعريف ارتباط؛
- السطر 60: تسمح لنا الخاصية [axios] بتكوين وحدة [@nuxtjs/axios] من السطر 52. لن نستخدم هذا الخيار، بل نفضل بدلاً من ذلك الخاصية [env] من السطر 88؛
- السطر 90: الحد الأقصى لوقت الانتظار لاستجابة من خادم حساب الضرائب؛
- السطر 91: مطلوب لعميل [nuxt] — يتيح استخدام ملفات تعريف الارتباط في الاتصالات مع خادم حساب الضرائب؛
- السطر 92: عنوان URL الأساسي لخادم حساب الضرائب؛
- السطر 94: مدة جلسة Nuxt (5 دقائق)؛
- السطر 77: سيتم التحكم في تنقل العميل وخادم [nuxt] بواسطة برمجيات وسيطة للتوجيه؛
15.4. طبقة [UI] للتطبيق

سنمنح تطبيق [nuxt] حق الوصول إلى واجهة برمجة تطبيقات خادم حساب الضرائب عبر العرض التالي:

- في [2]، القائمة التي توفر الوصول إلى واجهة برمجة تطبيقات خادم حساب الضرائب:
- [المصادقة]: تتوافق مع صفحة [المصادقة]. ترسل هذه الصفحة طلب مصادقة إلى خادم حساب الضرائب باستخدام بيانات الاعتماد [admin, admin]، وهي البيانات الوحيدة المصرح بها حاليًا. النتيجة المعروضة مشابهة لـ [3]؛
- [طلب بيانات الإدارة]: تتوافق مع صفحة [get-admindata]. تطلب هذه الصفحة بيانات من خادم حساب الضرائب — المشار إليها هنا بـ [adminData] — والتي تتيح حساب الضرائب. النتيجة المعروضة مشابهة لـ [3]؛
- [إنهاء جلسة الضرائب]: تتوافق مع صفحة [end-session]. ترسل هذه الصفحة طلب إنهاء الجلسة PHP إلى خادم حساب الضرائب. ثم يقوم الخادم بإلغاء جلسة PHP الحالية وتهيئة جلسة جديدة فارغة؛
15.5. طبقات [dao] لتطبيق [nuxt]
كما ذكر أعلاه، ستكون بنية تطبيق [nuxt] على النحو التالي:

- في [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 اللازمة؛
سنستخدم الإصدار 14 من خادم حساب الضرائب الذي تم تطويره في الوثيقة |مقدمة إلى PHP7 من خلال الأمثلة|. سنستخدم جزءًا فقط من واجهة برمجة التطبيقات (API) JSON الخاصة به:
الطلب | الاستجابة |
| |
| |
| |
| |
15.5.1. طبقة [DAO] لخادم [Nuxt]

سيستخدم خادم [node.js] [1] طبقة [DAO] الموضحة في الوثيقة |مقدمة إلى إطار عمل VUE.JS من خلال أمثلة|. وإليك الكود مرة أخرى:
'use strict';
// imports
import qs from 'qs'
class Dao {
// manufacturer
constructor(axios) {
this.axios = axios;
// session cookie
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// init session
async initSession() {
// query options HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: 'init-session',
type: 'json'
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// query options HHTP [post /main.php?action=authenticate-user]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// body of POST
data: qs.stringify({
user: user,
password: password
}),
// URL parameters
params: {
action: 'authentifier-utilisateur'
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// query options HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// URL parameters
params: {
action: 'get-admindata'
}
};
// execute query HTTP
const data = await this.getRemoteData(options);
// result
return data;
}
async getRemoteData(options) {
// for the session cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execute query HTTP
let response;
try {
// asynchronous request
response = await this.axios.request('main.php', options);
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response;
} else {
// error restart
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// retrieve the session cookie if it exists
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie is an array
// look for the session cookie in this table
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// look for the session cookie
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// the session cookie is stored
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// we found
trouvé = true;
} else {
// next item
i++;
}
}
}
// the server response is in [response.data]
return response.data;
}
}
// class export
export default Dao;
- تُرجع جميع الطرق في طبقة [dao] الكائن المرسل من خادم البيانات [{action: ‘xx’, status: nn, response: {...}] مع:
- [action]: اسم الإجراء الذي نفذه خادم البيانات؛
- [state]: مؤشر رقمي:
- [initSession]: الحالة=700 للإشارة إلى استجابة خالية من الأخطاء؛
- [authenticateUser]: الحالة = 200 للاستجابة بدون أخطاء؛
- [getAdminData]: الحالة = 1000 للاستجابة بدون أخطاء؛
- [end-session]: الحالة = 400 للاستجابة بدون أخطاء؛
- [response]: استجابة مرتبطة بالمؤشر الرقمي [status]. قد تختلف اعتمادًا على هذا المؤشر الرقمي؛
دعونا نفحص منشئ فئة [Dao]:
// constructeur
constructor(axios) {
this.axios = axios;
// cookie de session
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- السطر 2: يتم توفير الكائن [axios] الذي يتم تمريره كحجة إلى المنشئ بواسطة الكود المستدعي. وهذا الكائن هو الذي سيقوم بإجراء طلبات HTTP؛
- السطر 5: اسم ملف تعريف ارتباط الجلسة الذي أرسله خادم البيانات المكتوب بلغة PHP؛
- السطر 6: ملف تعريف الارتباط الخاص بالجلسة الذي يتم تبادله بين طبقة [dao] وخادم البيانات. يتم تهيئة هذا بواسطة الدالة [getRemoteData] في الأسطر 67–113؛
بالنسبة لملف تعريف الارتباط الخاص بالجلسة، نحتاج إلى النظر في طبقتين منفصلتين من [dao]:
- طبقة المتصفح؛
- طبقة الخادم؛
سنحتاج إلى إدارة ثلاث ملفات تعريف ارتباط للجلسة:
- ملف تعريف الارتباط الذي يتم تبادله بين عميل [nuxt] وخادم PHP 7؛
- الملف الذي يتم تبادله بين خادم [nuxt] وخادم PHP 7؛
- الملف الذي يتم تبادله بين عميل [nuxt] وخادم [nuxt]؛
سنضمن أن ملف تعريف الارتباط الخاص بالجلسة مع خادم PHP هو نفسه لكل من العميل وخادم [nuxt]. سنسمي ملف تعريف الارتباط هذا ملف تعريف الارتباط الخاص بجلسة PHP. ملف تعريف الارتباط هذا هو الملف المذكور في الحالتين 1 و 2. سنسمي ملف تعريف الارتباط المذكور في الحالة 3 ملف تعريف الارتباط الخاص بجلسة [nuxt]. وبالتالي سيكون لدينا جلستان:
- جلسة عمل PHP مع ملف تعريف ارتباط جلسة العمل PHP؛
- جلسة [nuxt] مع ملف تعريف ارتباط جلسة [nuxt]؛
لماذا نستخدم ملف تعريف الارتباط نفسه لجلسات PHP الخاصة بالعميل وجلسات متصفح [nuxt]؟ نريد أن يكون التطبيق قادرًا على التواصل مع خادم PHP 7 بغض النظر عما إذا كان العميل أو خادم [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] السابقة:
// constructeur
constructor(axios) {
this.axios = axios;
// cookie de session
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- السطران 5 و6 يتوافقان مع ملف تعريف ارتباط جلسة العمل في PHP مع خادم حساب الضرائب؛
إدارة ملف تعريف ارتباط جلسة PHP أعلاه غير مناسبة لخادم [nuxt]: حيث يتم إنشاء مثيل لطبقة [dao] الخاصة به مع كل طلب جديد يتم إرساله إلى خادم [nuxt]. تذكر أن طلب صفحة من خادم [nuxt] يؤدي فعليًا إلى إعادة تعيين تطبيق [nuxt]. وبالتالي، عندما يقوم خادم [nuxt] بإرسال طلبه الأول إلى خادم البيانات، يتم تهيئة ملف تعريف ارتباط جلسة PHP لطبقة [dao]، وتُفقد هذه القيمة أثناء طلب HTTP التالي من نفس خادم [nuxt]، لأنه في غضون ذلك تم إعادة إنشاء طبقة [dao] الخاصة به، وإعادة تنفيذ المنشئ، وإعادة تعيين ملف تعريف ارتباط جلسة PHP إلى سلسلة فارغة (السطر 6)؛
أحد الحلول هو استخدام مُنشئ مختلف لطبقة [dao] في الخادم:
// constructeur
constructor(axios, phpSessionCookie) {
// bibliothèque axios
this.axios = axios
// valeur du cookie de session
this.phpSessionCookie = phpSessionCookie
// nom du cookie de session du serveur PHP
this.phpSessionCookieName = 'PHPSESSID'
}
- السطر 2: هذه المرة، سيتم توفير ملف تعريف ارتباط جلسة عمل PHP إلى مُنشئ طبقة [DAO] لخادم البيانات؛
كيف سيقدم خادم [nuxt] ملف تعريف ارتباط جلسة عمل PHP هذا إلى مُنشئ طبقة [dao] الخاصة به؟ سنقوم بتخزين ملف تعريف ارتباط جلسة عمل PHP في ملف تعريف ارتباط جلسة عمل [nuxt] الذي يتم تبادله بين المتصفح وخادم [nuxt]. وتتم العملية على النحو التالي:
- يتم تشغيل تطبيق [nuxt]؛
- عندما يقوم خادم [nuxt] بإرسال أول طلب HTTP إلى خادم PHP، فإنه يخزن ملف تعريف ارتباط جلسة عمل PHP الذي تلقّاه في ملف تعريف ارتباط جلسة عمل [nuxt] الذي يتبادله مع عميل [nuxt]؛
- يستقبل المتصفح الذي يستضيف عميل [nuxt] ملف تعريف ارتباط جلسة [nuxt] هذا، وبالتالي يعيده تلقائيًا مع كل طلب جديد إلى خادم [nuxt]؛
- عندما يحتاج خادم [nuxt] إلى إرسال طلب جديد إلى خادم PHP، سيجد ملف تعريف ارتباط جلسة عمل PHP داخل ملف تعريف ارتباط جلسة عمل [nuxt] الذي أرسله المتصفح إليه. ثم سيرسله إلى خادم PHP؛
هناك بالفعل ملفان لملفات تعريف الارتباط للجلسة، ويجب عدم الخلط بينهما:
- ملف تعريف ارتباط جلسة [nuxt] الذي يتم تبادله بين خادم [nuxt] ومتصفح عميل [nuxt]؛
- ملف تعريف ارتباط جلسة عمل PHP الذي يتم تبادله بين خادم [nuxt] وخادم PHP أو بين عميل [nuxt] وخادم PHP؛
لنعد الآن إلى كود طريقة فئة [Dao]. لا تتضمن هذه الطريقة دالة لإغلاق جلسة PHP مع خادم حساب الضرائب. سنضيف هذه الدالة:
// end of tax calculation session
async finSession() {
// query options HHTP [get /main.php?action=end-session]
const options = {
method: 'GET',
// URL parameters
params: {
action: 'fin-session'
}
}
// execute query HTTP
const data = await this.getRemoteData(options)
// result
return data
}
أثناء الاختبار، اكتشفنا أن الدالة [getRemoteData] التي تم استدعاؤها في السطر 12 غير مناسبة لطريقة [finSession]:
async getRemoteData(options) {
// for the session cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execute query HTTP
let response;
try {
// asynchronous request
response = await this.axios.request('main.php', options);
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response;
} else {
// error restart
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// retrieve the session cookie if it exists
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie is an array
// look for the session cookie in this table
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// look for the session cookie
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// the session cookie is stored
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// we found
trouvé = true;
} else {
// next item
i++;
}
}
}
// the server response is in [response.data]
return response.data;
}
- الأسطر 30–43: نبحث عن ملف تعريف الارتباط [PHPSESSID=xxx]. إذا تم العثور عليه، يتم تخزينه في الفئة (السطر 36)؛
هذا الرمز غير مناسب لطريقة [finSession] الجديدة لأن خادم PHP يرسل، عند إجراء [fin-session]، ملفين من ملفات تعريف الارتباط باسم [PHPSESSID]. فيما يلي مثال تم الحصول عليه باستخدام عميل [Postman]:

- في [1]، الطلب من عميل [Postman]؛
- في [3]، استجابة خادم PHP؛
- في [4]، رؤوس HTTP لاستجابة خادم PHP؛

- في [5]، يُشير خادم PHP أولاً إلى أنه قد حذف جلسة PHP الحالية؛
- في [6]، يرسل خادم PHP ملف تعريف الارتباط الخاص بجلسة PHP الجديدة؛
مع الكود الحالي، تسترد الدالة [getRemoteData] ملف تعريف الارتباط [5]، في حين أن ملف تعريف الارتباط [6] هو الذي يجب تخزينه.
لذلك يجب علينا تحديث الكود الخاص بوظيفة [getRemoteData]:
async getRemoteData(options) {
// is there a PHP session cookie?
if (this.phpSessionCookie) {
// are there headers?
if (!options.headers) {
// create an empty object
options.headers = {}
}
// session cookie header PHP
options.headers.Cookie = this.phpSessionCookie
}
// execute query HTTP
let response
try {
// asynchronous request
response = await this.axios.request('main.php', options)
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response
} else {
// error restart
throw error
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// look for session cookie PHP in received cookies
// all cookies received
const cookies = response.headers['set-cookie']
if (cookies) {
// cookies is a picture
// look for the PHP session cookie in this array
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// look for the PHP session cookie
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// we store the PHP session cookie
const phpSessionCookie = results[1]
// is the word [deleted] in it?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// we have the right session cookie PHP
this.phpSessionCookie = phpSessionCookie
// we found
trouvé = true
} else {
// next item
i++
}
} else {
// next item
i++
}
}
}
// the server response is in [response.data]
return response.data
}
- السطر 41: عثرنا على ملف تعريف ارتباط باسم [PHPSESSID]. نقوم بتخزينه محليًا؛
- السطر 43: نتحقق مما إذا كان ملف تعريف الارتباط المحفوظ يحتوي على السلسلة [PHPSESSID=deleted]؛
- السطر 46: إذا كانت الإجابة لا، فهذا يعني أننا عثرنا على ملف تعريف الارتباط الصحيح [PHPSESSID]. نقوم بتخزينه في الفئة؛
بعد دالة [getRemoteData]، يتم تخزين ملف تعريف ارتباط جلسة عمل PHP في الفئة، في [this.phpSessionCookie]. ذكرنا أن الفئة يتم إنشاء مثيل لها مع كل طلب HTTP جديد من خادم [nuxt]. لذلك يجب استخراج ملف تعريف ارتباط جلسة عمل PHP من الفئة. للقيام بذلك، نضيف طريقة جديدة إليها:
// accès au cookie de la session PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
- يطلب خادم [nuxt] إجراءً من طبقة [dao] الخاصة به عن طريق تزويد منشئه بملف تعريف ارتباط جلسة عمل PHP، إن وُجد؛
- بمجرد اكتمال الإجراء، يسترد خادم [nuxt] ملف تعريف ارتباط جلسة عمل PHP المخزن بواسطة طبقة [dao] باستخدام طريقة [getPhpSessionCookie] السابقة. قد يكون ملف تعريف الارتباط هذا هو نفسه الملف السابق أو ملفًا مختلفًا. تحدث الحالة الأخيرة في حالتين:
- عند تنفيذ طريقة [initSession] (لم يكن هناك ملف تعريف ارتباط جلسة عمل PHP من قبل)؛
- عند تنفيذ طريقة [finSession] (يقوم خادم PHP بتغيير ملف تعريف ارتباط جلسة عمل PHP)؛
لاحظ ميزة خاصة تتعلق بملف تعريف ارتباط جلسة عمل PHP. لا يتلقى خادم [nuxt] دائمًا ملف تعريف الارتباط هذا من خادم PHP. في الواقع، يرسله خادم PHP مرة واحدة فقط. بعد ذلك، لا يرسله مرة أخرى. عند النظر إلى كود [getRemoteData] و [getPhpSessionCookie]، يمكننا أن نرى أنه عندما لا يرسل خادم PHP ملف تعريف ارتباط الجلسة، فإن دالة [getPhpSessionCookie] تُرجع ملف تعريف ارتباط جلسة PHP المقدم إلى المُنشئ. هكذا يرسل الخادم دائمًا إلى خادم PHP آخر ملف تعريف ارتباط جلسة PHP أرسله خادم PHP إليه.
15.5.2. طبقة [dao] لعميل [nuxt]

بالنسبة لعميل [nuxt] الذي يعمل في متصفح، نستخدم الكود من فئة [Dao] في الوثيقة |مقدمة إلى إطار عمل VUE.JS من خلال أمثلة|:
"use strict";
// imports
import qs from "qs";
class Dao {
// manufacturer
constructor(axios) {
this.axios = axios;
}
// init session
async initSession() {
// query options HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: "init-session",
type: "json"
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// query options HHTP [post /main.php?action=authenticate-user]
const options = {
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
// body of POST
data: qs.stringify({
user: user,
password: password
}),
// URL parameters
params: {
action: "authentifier-utilisateur"
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// query options HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// URL parameters
params: {
action: "get-admindata"
}
};
// execute query HTTP
const data = await this.getRemoteData(options);
// result
return data;
}
async getRemoteData(options) {
// execute query HTTP
let response;
try {
// asynchronous request
response = await this.axios.request("main.php", options);
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response;
} else {
// error restart
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// the server response is in [response.data]
return response.data;
}
}
// class export
export default Dao;
يختلف هذا الكود عن طبقة [dao] في خادم [nuxt] من حيث أنه لا يدير ملف تعريف ارتباط جلسة PHP مع خادم حساب الضرائب: فالمتصفح هو الذي يتولى ذلك.
سنقوم، كما فعلنا مع طبقة [dao] في خادم [nuxt]، بإضافة طريقة [finSession]:
// end of tax calculation session
async finSession() {
// query options HHTP [get /main.php?action=end-session]
const options = {
method: 'GET',
// URL parameters
params: {
action: 'fin-session'
}
}
// execute query HTTP
const data = await this.getRemoteData(options)
// result
return data
}
عندما ينفذ عميل [nuxt] هذه الطريقة، يتلقى، تمامًا مثل خادم [nuxt]، ملفين من ملفات تعريف الارتباط لجلسة عمل PHP. في الواقع، المتصفح هو الذي يتلقاهما، وهو يتعامل مع الموقف بشكل صحيح: فهو يحتفظ فقط بملف تعريف الارتباط الخاص بجلسة عمل PHP الجديدة التي بدأها خادم حساب الضرائب. لذا، في المرة التالية التي يرسل فيها عميل [nuxt] طلبًا إلى خادم PHP، سيكون ملف تعريف الارتباط لجلسة عمل PHP صحيحًا لأن المتصفح هو الذي يرسله. ومع ذلك، هناك مشكلة: خادم [nuxt] غير مدرك أن ملف تعريف ارتباط جلسة عمل PHP قد تغير. وفي اتصالاته مع خادم PHP، سيرسل ملف تعريف ارتباط جلسة عمل PHP لم يعد موجودًا، وهذا سيؤدي إلى حدوث مشكلات. يحتاج عميل [nuxt] إلى إخطار خادم [nuxt] بأن ملف تعريف ارتباط جلسة عمل PHP قد تغير وتمريره إلى الخادم. ونحن نعرف كيف يمكنه القيام بذلك: عبر ملف تعريف ارتباط جلسة عمل [nuxt]، وهو ملف تعريف الارتباط الذي يتم تبادله بين العميل وخادم [nuxt]. لدى عميل [nuxt] طريقتان على الأقل لاسترداد ملف تعريف ارتباط جلسة عمل PHP الجديد:
- عن طريق طلبه من المتصفح؛
- باستخدام طريقة [getRemoteData] الخاصة بالخادم، والتي تعرف كيفية استرداد ملف تعريف ارتباط جلسة عمل PHP الجديد؛
سنستخدم الحل الثاني لأنه جاهز بالفعل. تصبح طريقة [getRemoteData] الخاصة بعميل [nuxt] كما يلي:
async getRemoteData(options) {
// execute query HTTP
let response
try {
// asynchronous request
response = await this.axios.request('main.php', options)
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response
} else {
// error restart
throw error
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// look for session cookie PHP in received cookies
// all cookies received
const cookies = response.headers['set-cookie']
if (cookies) {
// cookies is a picture
// look for session cookie PHP in this array
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// look for the PHP session cookie
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// we store the PHP session cookie
const phpSessionCookie = results[1]
// is the word [deleted] in it?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// we have the right session cookie PHP
this.phpSessionCookie = phpSessionCookie
// we found
trouvé = true
} else {
// next item
i++
}
} else {
// next item
i++
}
}
}
// the server response is in [response.data]
return response.data
}
احتفظنا فقط بالكود الموجود في [getRemoteData] الذي يعالج استجابة خادم PHP لاسترداد ملف تعريف ارتباط جلسة عمل PHP. لم نحتفظ بالكود الذي تضمن ملف تعريف ارتباط جلسة عمل PHP في الطلب الموجه إلى خادم PHP لأن المتصفح الذي يستضيف عميل [nuxt] يتولى ذلك.
بمجرد حصول عميل [nuxt] على ملف تعريف ارتباط جلسة عمل PHP، يجب وضعه في جلسة عمل [nuxt] حتى يتمكن خادم [nuxt] من استخدامه. لا تتولى طبقة [dao] معالجة ذلك، ولكنها توفر الوصول عبر طريقة إلى ملف تعريف ارتباط جلسة عمل PHP الذي قامت بتخزينه:
// accès au cookie de la session 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] في جانب واحد فقط: فهي لا ترسل ملف تعريف ارتباط جلسة عمل PHP بنفسها، حيث يتولى المتصفح هذه المهمة. ومع ذلك، اخترنا الحفاظ على طبقتين [dao] منفصلتين لأن الأسباب الكامنة وراء تطبيقهما تختلف.
15.6. جلسة [nuxt]
![]()
سيتم تغليف جلسة [nuxt] (بين العميل وخادم Nuxt) في كائن [session] التالي:
/* eslint-disable no-console */
// définition de la session
const session = {
// contenu de la session
value: {
// store non initialisé
initStoreDone: false,
// valeur du store Vuex
store: ''
},
// sauvegarde de la session dans un cookie
save(context) {
// sauvegarde du store en session
this.value.store = context.store.state
console.log('nuxt-session save=', this.value)
// sauvegarde de la valeur de la session
context.app.$cookies.set('nuxt-session', this.value, { path: context.base, maxAge: context.env.maxAge })
},
// reset de la session
reset(context) {
console.log('nuxt-session reset')
// reset du store
context.store.commit('reset')
// sauvegarde du nouveau store en session et sauvegarde de la session
this.save(context)
}
}
// export de la session
export default session
- الأسطر 5–10: تحتوي الجلسة على خاصية واحدة فقط [value] مع خاصيتين فرعيتين:
- [initStoreDone]، والتي تشير إلى ما إذا كان المتجر قد تم تهيئته أم لا؛
- [store]: قيمة [store.state] لمخزن Vuex الخاص بالتطبيق؛
- الأسطر 12–18: تُستخدم طريقة [save] لحفظ جلسة [nuxt] في ملف تعريف ارتباط. هنا، نستخدم مكتبة [cookie-universal-nuxt] لإدارة ملف تعريف الارتباط. لاحظ اسم ملف تعريف ارتباط جلسة [nuxt]: [nuxt-session] (السطر 17)؛
- الأسطر 20-26: تعيد طريقة [reset] تعيين جلسة [nuxt]؛
- السطر 23: يتم إعادة تعيين مخزن Vuex ثم حفظه في الجلسة في السطر 25؛
15.7. مكونات إضافية لإدارة جلسة [nuxt]

15.7.1. ملحق إدارة جلسة [nuxt] لخادم [nuxt]
عند بدء تشغيل التطبيق، يتم تشغيل خادم [nuxt] أولاً. وبالتالي فهو مسؤول عن تهيئة جلسة [nuxt]. النص البرمجي [server/plgSession] هو كما يلي:
/* eslint-disable no-console */
// import de la session
import session from '@/entities/session'
export default (context, inject) => {
// gestion de la session serveur
console.log('[plugin server plgSession]')
// y-a-t-il une session existante ?
const value = context.app.$cookies.get('nuxt-session')
if (!value) {
// nouvelle session
console.log("[plugin server plgSession], démarrage d'une nouvelle session")
} else {
// session existante
console.log("[plugin server plgSession], reprise d'une session existante")
session.value = value
}
// on injecte une fonction dans [context, Vue] qui rendra la session courante
inject('session', () => session)
}
- السطر 4: استيراد كود جلسة [nuxt]؛
- السطر 11: استرداد قيمة ملف تعريف ارتباط جلسة [nuxt]؛
- الأسطر 12–15: إذا لم يكن ملف تعريف ارتباط جلسة [nuxt] موجودًا، فإن جلسة [nuxt] المستوردة في السطر 4 كافية. لا يوجد شيء آخر للقيام به؛
- الأسطر 15–19: إذا كان ملف تعريف ارتباط جلسة [nuxt] موجودًا، فإننا نقوم في السطر 18 بتخزين قيمته في الجلسة المستوردة في السطر 4؛
- السطر 22: تم تهيئة الجلسة أو استعادتها. نجعلها متاحة عبر الدالة [$session]؛
15.7.2. المكوّن الإضافي لإدارة جلسة [nuxt] للعميل [nuxt]
النص البرمجي [client/plgSession] هو كما يلي:
/* eslint-disable no-console */
// import de la session
import session from '@/entities/session'
export default (context, inject) => {
// gestion de la session client
console.log('[plugin client plgSession], reprise de la session [nuxt] du serveur')
// on récupère la session existante du serveur nuxt
session.value = context.app.$cookies.get('nuxt-session')
// on injecte une fonction dans [context, Vue] qui rendra la session courante
inject('session', () => session)
}
- السطر 4: يتم استيراد جلسة [nuxt]؛
- السطر 10: نسترد جلسة [nuxt] الحالية من ملف تعريف الارتباط [nuxt-session]؛
- السطر 13: نُرجع جلسة [nuxt] المستوردة في السطر 4 عبر الدالة المُدرجة [$session]؛
15.8. المكونات الإضافية لطبقات [dao]

15.8.1. ملحق طبقة [dao] لعميل [nuxt]
النص البرمجي [client/plgDao] هو كما يلي:
/* eslint-disable no-console */
// on crée un point d'accès à la couche [Dao]
import Dao from '@/api/client/Dao'
export default (context, inject) => {
// configuration axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
context.$axios.defaults.withCredentials = context.env.withCredentials
// instanciation de la couche [dao]
const dao = new Dao(context.$axios)
// injection d'une fonction [$dao] dans le contexte
inject('dao', () => dao)
// log
console.log('[fonction client $dao créée]')
}
- السطر 3: يتم استيراد طبقة [dao] لعميل [nuxt]؛
- الأسطر 6-8: نقوم بتكوين كائن [context.$axios]، الذي سيقوم بإجراء طلبات HTTP لطبقة [dao] لعميل [nuxt] باستخدام المعلومات الموجودة في ملف [nuxt.config]:
// environnement
env: {
// configuration axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// configuration du cookie de session [nuxt]
maxAge: 60 * 5
}
- السطر 10: يتم إنشاء مثيل لطبقة [dao] الخاصة بعميل [nuxt]؛
- السطر 12: يتم إدخال الدالة [$dao] في سياق العميل وصفحاته. توفر هذه الدالة الوصول إلى طبقة [dao] من السطر 10؛
لذا، للوصول إلى طبقة [dao] لعميل [nuxt] أثناء تشغيله، نكتب:
- [context.app.$dao()] حيث يكون السياق معروفًا؛
- [this.$dao()] في صفحة [Vue.js]؛
15.8.2. المكوّن الإضافي لطبقة [dao] لخادم [nuxt]
النص البرمجي [server/plgDao] هو كما يلي:
/* eslint-disable no-console */
// on crée un point d'accès à la couche [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// configuration axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// on récupère le cookie de session
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instanciation de la couche [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// injection d'une fonction [$dao] dans le contexte
inject('dao', () => dao)
// log
console.log('[fonction server $dao créée]')
}
- السطر 3: يتم استيراد طبقة [dao] من خادم [nuxt]؛
- السطران 6-7: يتم تكوين الكائن [context.$axios] لإجراء طلبات HTTP لطبقة [dao] لخادم [nuxt] باستخدام المعلومات الموجودة في ملف [nuxt.config]:
// environnement
env: {
// configuration axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// configuration du cookie de session [nuxt]
maxAge: 60 * 5
}
- السطر 9: استرداد مخزن التطبيق [nuxt]؛
- السطر 10: إذا كان المخزن موجودًا، فإننا نسترد ملف تعريف ارتباط جلسة عمل PHP لأننا نحتاجه لإنشاء مثيل لطبقة [dao] لخادم [nuxt]؛
- السطر 13: إنشاء مثيل لطبقة [dao] لخادم [nuxt]؛
- السطر 15: يتم إدخال الدالة [$dao] في سياق وصفحات خادم [nuxt]. توفر هذه الدالة الوصول إلى طبقة [dao] من السطر 13؛
لذا، للوصول إلى طبقة [dao] لخادم [nuxt] أثناء تشغيله، نكتب:
- [context.app.$dao()] حيث يكون السياق معروفًا؛
- [this.$dao()] في صفحة [Vue.js]؛
15.9. مخزن Vuex
![]()
سيخزن مخزن [Vuex] جميع البيانات التي يجب مشاركتها بين المكونات المختلفة للتطبيق [الصفحات، العميل، الخادم] دون أن تكون هذه البيانات تفاعلية.
/* eslint-disable no-console */
// awning status
export const state = () => ({
// session jSON started
jsonSessionStarted: false,
// authenticated user
userAuthenticated: false,
// session cookie PHP
phpSessionCookie: '',
// adminData
adminData: ''
})
// changes in the awning
export const mutations = {
// state replacement
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
},
// awning reset
reset() {
this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
}
}
// awning actions
export const actions = {
nuxtServerInit(store, context) {
// who executes this code?
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// init session
initStore(store, context)
}
}
function initStore(store, context) {
// store is the blind to be initialized
// retrieve the session
const session = context.app.$session()
// has the session already been initiated?
if (!session.value.initStoreDone) {
// start a new blind
console.log("nuxtServerInit, initialisation d'une nouvelle session")
// put the blind in the session
session.value.store = store.state
// the blind is now initialized
session.value.initStoreDone = true
} else {
console.log("nuxtServerInit, reprise d'un store existant")
// update the store with the session store
store.commit('replace', session.value.store)
}
// save the session
session.save(context)
// log
console.log('initStore terminé, store=', store.state)
}
البيانات المخزنة في المخزن هي كما يلي:
- السطر 6: سيتم تعيين [jsonSessionStarted] على "true" فور إتمام تهيئة جلسة JSON مع خادم PHP بنجاح، سواء تمت المبادرة بها من جانب العميل أو خادم [nuxt]. وعند الانتهاء من عملية التهيئة هذه، سيتم استرداد ملف تعريف الارتباط الخاص بالجلسة لخادم PHP وتخزينه في الخاصية [phpSessionCookie]، السطر 10؛
- السطر 8: سيتم تعيين [userAuthenticated] على true بمجرد نجاح المصادقة مع خادم PHP، سواء تمت بواسطة العميل أو خادم [nuxt]؛
- السطر 12: ستكون [adminData] هي قيمة [adminData] التي تم الحصول عليها من خادم PHP بمجرد نجاح المصادقة؛
- الأسطر 18-22: تعمل عملية [replace] على تهيئة الخصائص السابقة بخصائص كائن تم تمريره كمعلمة؛
- الأسطر 24–26: تعيد عملية [reset] خصائص المخزن إلى قيمها الأولية؛
- الأسطر 31–37: تفوض الدالة [nuxtServerInit] عملها إلى الدالة [initStore]؛
- الأسطر 39–60: وظيفة [initStore] لها دوران:
- إذا لم يتم تهيئة المخزن، يتم تهيئته وإضافته إلى الجلسة؛
- إذا كان المخزن قد تم تهيئته بالفعل، يتم استرداد قيمته من جلسة [nuxt]؛
- السطر 42: يتم استرداد جلسة nuxt؛
- السطر 44: نتحقق مما إذا كان المخزن قد تم تهيئته:
- إذا لم يكن كذلك، يتم وضع المخزن الأولي في الجلسة (السطر 48)؛
- ثم، في السطر 50، نشير إلى أن المخزن قد تم تهيئته؛
- الأسطر 51-55: إذا تم تهيئة المخزن، فإننا نستخدمه، في السطر 54، لتهيئة المخزن بالقيمة الموجودة في الجلسة؛
- السطر 57: في جميع الحالات، يتم حفظ الجلسة في ملف تعريف الارتباط [nuxt-session]، جنبًا إلى جنب مع المخزن الذي تحتوي عليه؛
15.10. المكوّن الإضافي [plgEventBus]

يهدف هذا المكون الإضافي إلى إتاحة ناقل الأحداث لعميل [nuxt] عبر دالة [$eventBus] يتم إدخالها في سياق عميل [nuxt]. لا توجد حاجة لإدخالها في سياق خادم [nuxt] لأن الخادم لا يمكنه معالجة الأحداث. ومع ذلك، فقد رأينا بالفعل أن إدخالها على جانب الخادم ثم استخدامها لا يتسبب في حدوث خطأ.
/* eslint-disable no-console */
// on crée un bus d'événements entre les vues
import Vue from 'vue'
export default (context, inject) => {
// le bus d'événements
const eventBus = new Vue()
// injection d'une fonction [$eventBus] dans le contexte
inject('eventBus', () => eventBus)
// log
console.log('[fonction $eventBus créée]')
}
لقد سبق أن تناولنا هذا المكون الإضافي في قسم الروابط. ستكون الدالة [$eventBus] متاحة للعميل عبر الترميز التالي:
- [context.app.$eventBus()] حيثما كان السياق متاحًا؛
- [this.$eventBus()] في صفحات [Vue.js] الخاصة بالعميل؛
15.11. مكونات تطبيق [Nuxt]

مكون [layout] هو المكون الموجود في الأمثلة السابقة:
<!-- view layout -->
<template>
<!-- line -->
<div>
<b-row>
<!-- three-column zone -->
<b-col v-if="left" cols="3">
<slot name="left" />
</b-col>
<!-- nine-column zone -->
<b-col v-if="right" cols="9">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// paramètres
props: {
left: {
type: Boolean
},
right: {
type: Boolean
}
}
}
</script>
مكون [navigation] هو كما يلي:
<template>
<!-- bootstrap menu with three options -->
<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]

15.12.1. [default]
تخطيط [default] هو التخطيط المستخدم في مثال [nuxt-11] في الفقرة المرتبطة:
<template>
<div class="container">
<b-card>
<!-- un message -->
<b-alert show variant="success" align="center">
<h4>[nuxt-12] : requêtes HTTP avec axios</h4>
</b-alert>
<!-- la vue courante du routage -->
<nuxt />
<!-- message d’attente -->
<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>
<!-- erreur d’une opération asynchrone -->
<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
}
},
// life cycle
beforeCreate() {
console.log('[default beforeCreate]')
},
created() {
console.log('[default created]')
if (process.client) {
// listen to the evt [loading]
this.$eventBus().$on('loading', this.mShowLoading)
// and event [errorLoadingMessage]
this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
}
},
beforeMount() {
console.log('[default beforeMount]')
},
mounted() {
console.log('[default mounted]')
},
methods: {
// message waiting management
mShowLoading(value) {
console.log('[default mShowLoading], showLoading=', value)
this.showLoading = value
},
// asynchronous operation error
mShowErrorLoading(value, errorLoadingMessage) {
console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
this.showErrorLoading = value
this.errorLoadingMessage = errorLoadingMessage
}
}
}
</script>
- الأسطر 10–14: عرض رسالة تشير إلى أن العميل [nuxt] ينتظر اكتمال عملية غير متزامنة؛
- الأسطر 15–18: عرض أي رسالة خطأ ناتجة عن عملية غير متزامنة؛
- السطر 37: يتم تنفيذ الدالة [created] للصفحة [default] قبل الدالة [mounted] للصفحات؛
- السطر 39: إذا كان المنفذ هو عميل [nuxt]، فإن الصفحة [default] تستمع إلى
- [loading]، التي تشير إلى بداية أو نهاية فترة الانتظار. ثم يتم تنفيذ الدالة [mShowLoading]؛
- [errorLoading]، والتي تشير إلى ضرورة عرض رسالة خطأ. ثم يتم تنفيذ الدالة [mShowErrorLoading]؛
- صفحات [nuxt]:
- تعرض رسالة التحميل عن طريق إصدار الحدث [‘loading’, true] على ناقل الأحداث؛
- إخفاء رسالة التحميل عن طريق إرسال الحدث [‘loading’, false] على ناقل الأحداث؛
- عرض رسالة خطأ عن طريق إرسال الحدث [‘errorLoading’, true] على ناقل الأحداث؛
- إخفاء رسالة الخطأ عن طريق إصدار الحدث [‘errorLoading’, false] على ناقل الأحداث؛
15.12.2. [error]
يعرض تخطيط [error] رسالة خطأ في النظام (لا يديرها المطور):
<!-- définition HTML de la vue -->
<template>
<!-- mise en page -->
<Layout :left="true" :right="true">
<!-- alerte dans la colonne de droite -->
<template slot="right">
<!-- message sur fond rose -->
<b-alert show variant="danger" align="center">
<h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
</b-alert>
</template>
<!-- menu de navigation dans la colonne de gauche -->
<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 used
components: {
Layout,
Navigation
},
// property [props]
props: { error: { type: Object, default: () => 'waiting ...' } },
// life cycle
beforeCreate() {
// client and server
console.log('[error beforeCreate]')
},
created() {
// client and server
console.log('[error created, error=]', this.error)
},
beforeMount() {
// customer only
console.log('[error beforeMount]')
},
mounted() {
// customer only
console.log('[error mounted]')
}
}
</script>
15.13. الصفحة [index] التي يقدمها خادم [nuxt]

تتميز صفحة [index.vue] بأنها لا يمكن الوصول إليها إلا عبر خادم [nuxt]. ولا يتوفر للمستخدم أي رابط للوصول إليها عبر عميل [nuxt]. وفيما يلي شفرة البرمجة الخاصة بها:
<!-- page principale -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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 used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[index asyncData started]')
try {
// start a jSON session
const dao = context.app.$dao()
const response = await dao.initSession()
// log
console.log('[index asyncData response=]', response)
// retrieve session cookie PHP for future requests
const phpSessionCookie = dao.getPhpSessionCookie()
// we store the PHP session cookie in the [nuxt] session
context.store.commit('replace', { phpSessionCookie })
// was there a mistake?
if (response.état !== 700) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// note that the jSON session has started
context.store.commit('replace', { jsonSessionStarted: true })
// we return the result
return { result: '[succès]' }
} catch (e) {
// log
console.log('[index asyncData error=]', e)
// note that session jSON has not started
context.store.commit('replace', { jsonSessionStarted: false })
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// save the blind
const session = context.app.$session()
session.save(context)
// log
console.log('[index asyncData finished]')
}
},
// life cycle
beforeCreate() {
console.log('[index beforeCreate]')
},
created() {
console.log('[index created]')
},
beforeMount() {
console.log('[index beforeMount]')
},
mounted() {
console.log('[index mounted]')
// customer only
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- السطر 7: تعرض الصفحة نتيجة [result] لطلب غير متزامن (السطران 46 و51)؛
- السطر 31: العملية غير المتزامنة هي فتح جلسة JSON مع خادم حساب الضرائب؛
- السطر 25: نعلم أنه عند طلب الصفحة مباشرة من خادم [nuxt]، يتم تنفيذ دالة [asyncData] بواسطة الخادم فقط وليس بواسطة عميل [nuxt]، الذي يعمل بمجرد استلام المتصفح للاستجابة من خادم [nuxt]؛
- السطر 30: نسترد طبقة [dao] من سياق خادم [nuxt]؛
- السطر 35: إذا لم يكن الخادم قد أرسل طلبًا إلى خادم حساب الضرائب بعد، فإنه يتلقى ملف تعريف ارتباط جلسة PHP الأول؛ وإلا، فإنه يتلقى آخر ملف تعريف ارتباط جلسة PHP تلقّاه (انظر كود طبقة [dao] لخادم [nuxt] في القسم المرتبط)؛
- السطر 37: يتم تخزين ملف تعريف ارتباط جلسة PHP هذا في المخزن؛
- الأسطر 39-42: نتحقق مما إذا كانت العملية ناجحة. إذا لم تكن كذلك، يتم إصدار استثناء، والذي سيتم التقاطه بواسطة [catch] في السطر 47؛
- السطر 44: نسجل في المخزن أن جلسة JSON مع خادم PHP قد بدأت؛
- السطر 46: يتم إرجاع النتيجة [result] وعرضها في السطر 7؛
- الأسطر 47-54: معالجة أي استثناءات. يمكن أن تكون هذه الاستثناءات من نوعين:
- فشلت عملية HTTP في السطر 31 بسبب خطأ في الاتصال بين خادم [nuxt] وخادم PHP؛
- نجحت عملية HTTP في السطر 31 ولكن النتيجة المستلمة أبلغت عن خطأ (السطور 39-42)؛
- السطر 51: نلاحظ أن جلسة JSON مع خادم PHP لم تبدأ؛
- السطر 53: يتم إرجاع النتيجة [result] وعرضها في السطر 7. بالإضافة إلى ذلك، يتم تعيين الخصائص [showErrorLoading] و [errorLoadingMessage]، والتي سيستخدمها عميل [nuxt] لعرض رسالة خطأ عند استلامه للصفحة المرسلة من خادم [nuxt] (الأسطر 72–79)؛
- الأسطر 54–60: يتم تنفيذ الكود في جميع الحالات (النجاح أو الفشل)؛
- السطر 56: نسترد جلسة [nuxt] من سياق خادم [nuxt]؛
- السطر 57: نقوم بحفظها؛
- الأسطر 63–68: بمجرد انتهاء وظيفة [asyncData]، يقوم خادم [nuxt] بتنفيذ وظيفتي [beforeCreate] و [create]؛
ملاحظة: قد يفشل تنفيذ خادم [nuxt] لصفحة [index]، على سبيل المثال، إذا لم يكن خادم حساب الضرائب قيد التشغيل عند تشغيل تطبيق [nuxt]:

في هذه الحالة، الحل الوحيد هو بدء تشغيل خادم حساب الضرائب ثم تطبيق [nuxt] نفسه، نظرًا لأن قائمة التنقل لا توفر خيارًا لبدء جلسة JSON مع خادم حساب الضرائب؛
15.14. الصفحة [index] التي ينفذها عميل [nuxt]
لا يتم تنفيذ صفحة [index] من قِبل عميل [nuxt] إلا بعد أن يرسلها خادم [nuxt] إلى العميل. وقد أرسل الخادم معلومات [result]، وكذلك [showErrorLoading] و[errorLoadingMessage] إن وجدتا.
نعلم أن دالة [asyncData] لن يتم تنفيذها. وهذا يترك دورات الحياة، ولا سيما دالة [mounted]:
mounted() {
console.log('[index mounted]')
// client seulement
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
- يقوم عميل [nuxt] تلقائيًا بتضمين عنصر [result]، وإذا كان ذلك ممكنًا، عناصر [showErrorLoading] و[errorLoadingMessage] التي أرسلها إليه خادم [nuxt] في خصائص الصفحة:
- يتم عرض خاصية [result] في السطر 7؛
- يتم استخدام الخصائص [showErrorLoading، errorLoadingMessage] بواسطة الطريقة [mounted]: في السطر 4، يتم التحقق من الخاصية [showErrorLoading]. إذا كانت صحيحة، في السطر 6، يتم استخدام ناقل أحداث [nuxt] الخاص بالعميل للإشارة إلى وجود رسالة خطأ لعرضها؛
- يتم اعتراض الحدث [errorLoading] الذي تم تشغيله في السطر 6 بواسطة الصفحة [layouts/default] الموضحة في القسم المرتبط؛
15.15. صفحة [authentication] التي يتم تنفيذها بواسطة خادم [nuxt]
تتولى صفحة [authentication] مسؤولية مصادقة المستخدم مع خادم حساب الضرائب. وفيما يلي كودها:
<!-- page d’authentification -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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 used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[authentification asyncData started]')
if (process.client) {
// start waiting for customer [nuxt]
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// authenticate to the server
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// log
console.log('[authentification asyncData response=]', response)
// result
const userAuthenticated = response.état === 200
// we note whether the user is authenticated or not
context.store.commit('replace', { userAuthenticated })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// authentication error?
if (!userAuthenticated) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// we return the result
return { result: '[succès]' }
} catch (e) {
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[authentification asyncData finished]')
if (process.client) {
// end customer waiting [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// customer only
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- السطر 7: تعرض الصفحة نتيجة [result] الطلب غير المتزامن [asyncData] من الأسطر 25–65؛
- الأسطر 28–33: لا يقوم الخادم بتنفيذ هذه الأسطر المخصصة للعميل [nuxt]؛
- السطر 36: يتم استرداد طبقة [dao] من خادم [nuxt]؛
- السطر 37: نقوم بالمصادقة مع خادم حساب الضرائب باستخدام بيانات اعتماد الاختبار [admin, admin]، وهي البيانات الوحيدة التي يقبلها خادم حساب الضرائب؛
- السطر 41: لا تنجح عملية المصادقة إلا إذا كانت حالة الاستجابة 200؛
- السطر 43: نقوم بتعيين الخاصية [userAuthenticated] في المخزن؛
- الأسطر 44-46: يتم حفظ المخزن في جلسة [nuxt]؛
- الأسطر 48-51: إذا فشلت المصادقة، يتم إلقاء استثناء مع رسالة الخطأ المرسلة من خادم حساب الضرائب؛
- وإلا، في السطر 53، يتم إرجاع نتيجة ناجحة سيتم عرضها في السطر 7؛
- الأسطر 54-57: في حالة حدوث خطأ، يتم تعيين ثلاث خصائص للصفحة [result، showErrorLoading، errorLoadingMessage]. سيتم عرض الخاصية [result] في السطر 7. سيتم إرسال الخصائص الثلاث إلى العميل [nuxt]؛
- الأسطر 60-63: لا يتم تنفيذها بواسطة خادم [nuxt]؛
- بمجرد أن تعيد [asyncData] نتيجتها، يتم عرضها في السطر 7. ثم يتم تنفيذ طريقتي [beforeCreate] (السطور 67–69) و[created] (السطور 70–72)؛
- هذا كل شيء؛
ملاحظة: قد تفشل صفحة [authentification] في التنفيذ على خادم [nuxt]، على سبيل المثال، إذا لم يتم تهيئة جلسة JSON مع خادم حساب الضرائب. يمكن القيام بذلك على النحو التالي:
- احذف ملف تعريف ارتباط جلسة عمل PHP من متصفحك (للبدء من الصفر):

- قم بتشغيل تطبيق [nuxt] بينما لم يتم تشغيل خادم الحساب: ستظهر لك رسالة خطأ؛
- قم بتشغيل خادم حساب الضرائب؛
- أدخل عنوان URL [/authentication] مباشرةً في شريط عنوان المتصفح:

في هذه الحالة، الحل الوحيد هو إعادة تحميل صفحة [index] مرة أخرى.
15.16. صفحة [authentication] التي يتم تنفيذها بواسطة عميل [nuxt]
دعونا نلقي نظرة على كود الصفحة مرة أخرى:
<!-- page d’authentification -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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 used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[authentification asyncData started]')
if (process.client) {
// start waiting for customer [nuxt]
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// authenticate to the server
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// log
console.log('[authentification asyncData response=]', response)
// result
const userAuthenticated = response.état === 200
// we note whether the user is authenticated or not
context.store.commit('replace', { userAuthenticated })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// authentication error?
if (!userAuthenticated) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// we return the result
return { result: '[succès]' }
} catch (e) {
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[authentification asyncData finished]')
if (process.client) {
// end customer waiting [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// customer only
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
هناك حالتان يتم فيهما تنفيذ صفحة [المصادقة] بواسطة عميل [nuxt]:
- يتم تشغيل عميل [nuxt] بعد أن يرسل خادم [nuxt] صفحة [المصادقة] إلى متصفح عميل [nuxt]؛
- عميل [nuxt] لأن المستخدم نقر على رابط [Authentication] في قائمة التنقل:

دعونا أولاً ندرس السيناريو الأول. في هذه الحالة، لا يقوم عميل [nuxt] بتنفيذ دالة [asyncData]. بل يدمج عنصر [result]، وعند الاقتضاء، عناصر [showErrorLoading] و[errorLoadingMessage] التي أرسلها إليه خادم [nuxt] في خصائص الصفحة:
- يتم عرض خاصية [result] في السطر 7؛
- يتم استخدام خصائص [showErrorLoading، errorLoadingMessage] بواسطة طريقة [mounted]: في السطر 79، يتم التحقق من خاصية [showErrorLoading]. إذا كانت صحيحة، في السطر 81، يتم استخدام ناقل أحداث عميل [nuxt] للإشارة إلى وجود رسالة خطأ لعرضها؛
تم شرح آلية عرض رسالة الخطأ لصفحة [index] في قسم الروابط.
الحالة 2 هي عندما يعمل عميل [nuxt] بعد أن ينقر المستخدم على رابط [Authentication]. في هذه الحالة، يعمل عميل [nuxt] بشكل مستقل وليس بعد خادم [nuxt]. ثم يتم تنفيذ الدالة [asyncData]. نقدم فقط التفاصيل التي تختلف عن التفسيرات المقدمة للصفحة التي ينفذها خادم [nuxt]:
- الأسطر 28-33: يطلب عميل [nuxt] عرض رسالة التحميل ومسح أي رسالة خطأ تم عرضها مسبقًا؛
- السطر 36: يتم الآن استرداد طبقة [dao] لعميل [nuxt] هنا؛
- الأسطر 60-63: يطلب عميل [nuxt] إزالة رسالة التحميل؛
- بمجرد انتهاء [asyncData]، ستستمر دورة حياة الصفحة. سيتم تنفيذ الدالة [mounted] في الأسطر 76–83. إذا حدث خطأ، فسيتم عرض رسالة الخطأ؛
ملاحظة: لإحداث خطأ، اتبع الإجراء الموضح لخادم [nuxt] في نهاية قسم الروابط، ولكن بدلاً من طلب صفحة [authentication] عن طريق كتابة عنوان URL الخاص بها في شريط العناوين، استخدم رابط [Authentication] في قائمة التنقل. سيؤدي هذا إلى تشغيل عميل [nuxt].
15.17. صفحة [get-admindata]
فيما يلي كود صفحة [get-admindata]:
<!-- vue get-admindata -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message -->
<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 used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[get-admindata asyncData started]')
if (process.client) {
// start waiting
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// the data [admindata] is requested
const response = await context.app.$dao().getAdminData()
// log
console.log('[get-admindata asyncData response=]', response)
// result
const adminData = response.état === 1000 ? response.réponse : ''
// put the data in the store
context.store.commit('replace', { adminData })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// was there a mistake?
if (!adminData) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// return the value received
return { result: adminData }
} catch (e) {
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[get-admindata asyncData finished]')
if (process.client) {
// end waiting
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[get-admindata beforeCreate]')
},
created() {
console.log('[get-admindata created]')
},
beforeMount() {
console.log('[get-admindata beforeMount]')
},
mounted() {
console.log('[get-admindata mounted]')
// customer
if (this.showErrorLoading) {
console.log('[get-admindata mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
هذه الصفحة تشبه إلى حد كبير صفحة [authentication]. التفسيرات هي نفسها سواء تم تنفيذها بواسطة خادم [nuxt] أو بواسطة عميل [nuxt]. لاحظ، مع ذلك، أن السطر 7 لا يعرض النجاح/الفشل كما في السابق، بل يعرض قيمة البيانات المستلمة من خادم حساب الضرائب (السطر 52):

يتم الحصول على النتيجة أعلاه باستخدام كل من الخادم وعميل [nuxt]. لإحداث خطأ، اطلب صفحة [get-admindata] — إما عبر الخادم أو عميل [nuxt] — دون المصادقة:

15.18. صفحة [fin-session]
فيما يلي كود الصفحة:
<!-- page principale -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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 used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[fin-session asyncData started]')
// customer case [nuxt]
if (process.client) {
// start waiting
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// a new session PHP is requested from the tax calculation server
const dao = context.app.$dao()
const response = await dao.finSession()
// log
console.log('[fin-session asyncData response=]', response)
// was there a mistake?
if (response.état !== 400) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// the server has sent a new session cookie PHP
// we retrieve it for both the server and the nuxt client
// if this code is executed by the [nuxt] client, the PHP session cookie must be set in the nuxt session
// so that the [plgDao] plugin on the [nuxt] server can retrieve it and initialize the [dao] layer with
// if this code is executed by the [nuxt] server, the PHP session cookie must be set in the nuxt session
// so that the client routing [nuxt] can retrieve it and pass it to the browser
const phpSessionCookie = dao.getPhpSessionCookie()
// we note in the store that the session jSON has been started and we store the session cookie PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// we return the result
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// log
console.log('[fin-session asyncData error=]', e)
// report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[fin-session asyncData finished]')
if (process.client) {
// end waiting
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[fin-session beforeCreate]')
},
created() {
console.log('[fin-session created]')
},
beforeMount() {
console.log('[fin-session beforeMount]')
},
mounted() {
console.log('[fin-session mounted]')
// customer only
if (this.showErrorLoading) {
console.log('[fin-session mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
يشبه هذا الكود إلى حد كبير الكود الموجود في الصفحات السابقة، والتفسيرات هي نفسها. هناك نقطة واحدة فقط يجب ملاحظتها: تؤدي العملية غير المتزامنة في السطر 38 إلى قيام خادم حساب الضرائب بإرسال ملف تعريف ارتباط جلسة عمل PHP جديد. تختلف تعليمات التعامل مع ملف تعريف الارتباط هذا اعتمادًا على ما إذا كان الخادم أو عميل [nuxt] هو الذي ينفذ هذا الكود.
لنبدأ بخادم [nuxt]:
- السطر 37: هذه هي طبقة [dao] لخادم [nuxt] التي يتم إنشاء مثيل لها. دعونا نستذكر كود منشئها:
// constructeur
constructor(axios, phpSessionCookie) {
// bibliothèque axios
this.axios = axios
// valeur du cookie de session
this.phpSessionCookie = phpSessionCookie
// nom du cookie de session du serveur PHP
this.phpSessionCookieName = 'PHPSESSID'
}
في السطر 1، نرى أن المنشئ يحتاج إلى ملف تعريف ارتباط جلسة PHP الحالي — آخر ملف تم استلامه، سواء من الخادم أو من عميل [nuxt]؛
- السطر 52: يسترد خادم [nuxt] ملف تعريف الارتباط الخاص بجلسة PHP الجديدة أو ملف تعريف الارتباط القديم إذا فشلت عملية إنهاء الجلسة؛
- السطر 54: يتم وضع ملف تعريف ارتباط جلسة عمل PHP في المخزن ثم يتم حفظه في جلسة عمل [nuxt] في السطرين 56–57؛
- بعد الخادم، يقوم عميل [nuxt] بتنفيذ صفحة [end-session] بالبيانات المرسلة من الخادم. ونعلم أنه لن يقوم بتنفيذ دالة [asyncData]؛
- في النهاية، بعد أن ينتهي الخادم والعميل [nuxt] من عملهما، نعلم أن ملف تعريف الارتباط PHP المطلوب للتواصل مع خادم حساب الضرائب موجود في جلسة [nuxt]؛
حقيقة أن ملف تعريف الارتباط PHP موجود في جلسة [nuxt] كافية للخادم، لأن ذلك هو المكان الذي ستسترده منه طبقة [dao] الخاصة به. في المكون الإضافي [server/plgDao] الذي يقوم بتهيئة طبقة [dao] الخاصة بالخادم، كتبنا:
/* eslint-disable no-console */
// on crée un point d'accès à la couche [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// configuration axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// on récupère le cookie de session
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instanciation de la couche [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// injection d'une fonction [$dao] dans le contexte
inject('dao', () => dao)
// log
console.log('[fonction server $dao créée]')
}
- السطر 13: يتم إنشاء مثيل لطبقة [dao] لخادم [nuxt] باستخدام ملف تعريف ارتباط جلسة العمل PHP المأخوذ من جلسة عمل [nuxt]، السطور 9–10؛
أما بالنسبة لعميل [nuxt]، فالأمر يختلف. فليس العميل هو الذي يرسل ملف تعريف الارتباط، بل المتصفح هو الذي ينفذه. ومع ذلك، فإن هذا المتصفح لا يعرف ملف تعريف الارتباط الخاص بجلسة PHP الجديدة التي استلمها خادم [nuxt]. إذا استخدمنا الروابط الموجودة في قائمة التنقل [3]:

سيتلقى خادم حساب الضرائب ملف تعريف ارتباط جلسة عمل PHP قديمًا من المتصفح وسيرد بأنه لا توجد جلسة عمل JSON مرتبطة بملف تعريف الارتباط هذا. نحتاج إلى إيجاد طريقة لتمرير ملف تعريف ارتباط جلسة عمل PHP الجديد إلى المتصفح.
يمكننا استخدام برمجيات وسيطة للتوجيه للقيام بذلك:

البرنامج النصي [client/routing] هو برنامج الوسيط للتوجيه المُعلن في ملف [nuxt.config]:
// router
router: {
// application URL root
base: '/nuxt-12/',
// routing middleware
middleware: ['routing']
},
النص البرمجي [middleware/routing] هو كما يلي:
/* eslint-disable no-console */
// on importe le middleware du client
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.client) {
// routage client
clientRouting(context)
}
}
- الأسطر 9–12: نقوم بتوجيه العميل فقط باستخدام دالة مستوردة في السطر 4؛
النص البرمجي [middleware/client/routing] هو كما يلي:
/* 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
}
}
لنعد إلى الوضع الذي يلي مباشرةً تنفيذ صفحة [fin-session] بواسطة خادم [nuxt]:

إذا نقرت على أحد الروابط في القائمة [3]، سيتولى عميل [nuxt] زمام الأمور. وبما أنه سيكون هناك تغيير في الصفحة، فسيتم تنفيذ برنامج توجيه العميل:
- السطر 13: يتم العثور على ملف تعريف ارتباط جلسة عمل PHP في مخزن تطبيق [nuxt]؛
- السطر 14: إذا لم تكن فارغة، يتم إرسالها إلى المتصفح (السطر 16). من هذه النقطة فصاعدًا، يمتلك متصفح عميل [nuxt] ملف تعريف ارتباط جلسة عمل PHP الصحيح؛
يتم تشغيل البرنامج النصي [client/routing] في كل مرة يغير فيها عميل [nuxt] الصفحات. يعمل كود البرنامج النصي بغض النظر عن الصفحة المستهدفة: في الأساس، في معظم الأحيان، يرسل للمتصفح ملف تعريف ارتباط جلسة عمل PHP موجود لديه بالفعل، باستثناء حالتين:
- مباشرة بعد بدء تشغيل التطبيق، يقوم خادم [nuxt] بتنفيذ صفحة [index] ويتلقى ملف تعريف ارتباط جلسة عمل PHP الأول الذي لا يمتلكه متصفح عميل [nuxt]؛
- عندما يقوم خادم [nuxt] بتنفيذ صفحة [end-session] كما هو موضح للتو؛
الآن دعونا نفحص الحالة التي يتم فيها تنفيذ صفحة [end-session] بواسطة عميل [nuxt] فقط، لأن المستخدم نقر على الرابط الخاص بها في قائمة التنقل. الآن أصبح عميل [nuxt] هو الذي ينفذ دالة [asyncData]:
try {
// a new session PHP is requested from the tax calculation server
const dao = context.app.$dao()
const response = await dao.finSession()
// log
console.log('[fin-session asyncData response=]', response)
// was there a mistake?
if (response.état !== 400) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// the server has sent a new session cookie PHP
// we retrieve it for both the server and the nuxt client
// if this code is executed by the [nuxt] client, the PHP session cookie must be set in the nuxt session
// so that the [plgDao] plugin on the [nuxt] server can retrieve it and initialize the [dao] layer with
// if this code is executed by the [nuxt] server, the PHP session cookie must be set in the nuxt session
// so that the client routing [nuxt] can retrieve it and pass it to the browser
const phpSessionCookie = dao.getPhpSessionCookie()
// we note in the store that the session jSON has been started and we store the session cookie PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// we return the result
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// log
console.log('[fin-session asyncData error=]', e)
// report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[fin-session asyncData finished]')
if (process.client) {
// end waiting
context.app.$eventBus().$emit('loading', false)
}
}
- السطر 3: هذه هي طبقة [dao] لعميل [nuxt] التي يتم استردادها هنا؛
- السطر 18: يتم تخزين ملف تعريف ارتباط جلسة عمل PHP الذي تم استرداده بواسطة طبقة [dao] لعميل [nuxt]، ووضعه في المخزن (السطر 20)، ثم حفظه في جلسة عمل [nuxt] (السطران 22-23)؛
- منذ ذلك الحين، يعمل كل شيء على ما يرام لأننا نعلم أن طبقة [dao] لخادم [nuxt] ستسترد ملف تعريف ارتباط جلسة عمل PHP من جلسة عمل [nuxt]؛
15.19. التنفيذ
لتشغيل هذا المثال، يجب أولاً حذف ملف تعريف ارتباط جلسة [nuxt] وملف تعريف ارتباط PHP من المتصفح الذي يشغل عميل [nuxt] للبدء من الصفر. فيما يلي مثال باستخدام متصفح Chrome:

15.20. الخلاصة
كان هذا المثال معقدًا بشكل خاص. فقد جمع بين المعرفة المكتسبة في الأمثلة السابقة: تخزين الاستمرارية في جلسة [nuxt]، ومكونات إضافية لحقن الوظائف، وبرمجيات وسيطة للتوجيه، ومعالجة الأخطاء للعمليات غير المتزامنة. زادت التعقيدات بسبب رغبتنا في تمكين المستخدم من استخدام روابط قائمة التنقل وكتابة عناوين URL يدويًا دون تعطيل التطبيق. لتحقيق ذلك، كان علينا فحص سلوك كل صفحة اعتمادًا على ما إذا كانت تُنفَّذ بواسطة العميل أو الخادم [nuxt].
هذا الاتساق في سلوك العميل وخادم [Nuxt] ليس ضروريًا. ضع في اعتبارك السيناريو الشائع التالي:
- يتم تقديم الصفحة الأولى بواسطة خادم [nuxt]؛
- يتم تقديم جميع الصفحات اللاحقة بواسطة عميل [nuxt]، الذي يعمل بعد ذلك في وضع [SPA]؛
ومع ذلك، حتى في هذه الحالة، يجب عليك التحقق من كيفية عرض جميع الصفحات عند تنفيذها بواسطة خادم [nuxt]، لأن هذا هو ما ستراه محركات البحث عند طلبها.