Skip to content

15. Esempio [nuxt-12]: richieste HTTP con axios

15.1. Introduzione

In questo nuovo esempio, vedremo come effettuare richieste HTTP utilizzando la libreria [axios] all'interno delle funzioni [asyncData]. Inoltre, applicheremo i concetti già trattati:

  • l'uso dei plugin dall'esempio [nuxt-06]:
  • la memorizzazione dello store in un cookie di sessione dall'esempio [nuxt-06];
  • il controllo della navigazione utilizzando il middleware dall'esempio [nuxt-09];
  • gestione degli errori dall'esempio [nuxt-11];

L'architettura dell'esempio sarà la seguente:

Image

  • L'applicazione [nuxt] sarà ospitata sul server [node.js] [3], scaricata dal browser [1], che provvederà poi a eseguirla;
  • sia il client [nuxt] [1] che il server [nuxt] [3] effettueranno richieste HTTP al server dati [2]. Questo server sarà il server di calcolo delle imposte sviluppato nella sezione PHP 7. Useremo la sua versione più recente, la versione 14, con le richieste CORS abilitate;

L'architettura dell'esempio può essere semplificata come segue:

Image

  • In [1], il server [node.js] fornisce le pagine [nuxt] al browser [2]. È il livello [web] [8] del server che fornisce queste pagine. Per fornire la pagina, il server potrebbe aver richiesto dati esterni dal server dati [3]. È il livello [DAO] [9] che effettua le richieste HTTP necessarie;
  • Ad ogni richiesta di pagina al server [Node.js] [1], il browser [2] riceve l'intera applicazione [Nuxt], che poi viene eseguita in modalità SPA. Il blocco [UI] (Interfaccia Utente) [4] mostra le pagine [Vue.js] all'utente. Le azioni dell'utente o il ciclo di vita naturale delle pagine possono innescare richieste di dati esterni al server dati [3]. Il livello [DAO] [5] effettua quindi le richieste HTTP necessarie;

15.2. Struttura delle directory del progetto

Image

15.3. Il file di configurazione [nuxt.config.js]

Il progetto sarà gestito dal seguente file [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
  }
}
  • riga 22: gestiamo noi stessi la notifica di completamento di un'azione asincrona;
  • riga 31: useremo vari plugin specializzati o per il client o per il server, ma non per entrambi contemporaneamente;
  • riga 52: il modulo [axios] è integrato in [nuxt]. Di conseguenza, l'oggetto [axios], che gestirà le richieste HTTP dall'applicazione [nuxt] al server PHP di calcolo delle imposte, sarà disponibile in [context.$axios];
  • riga 54: il modulo [cookie-universal-nuxt] ci permetterà di salvare la sessione [nuxt] in un cookie;
  • riga 60: la proprietà [axios] ci permette di configurare il modulo [@nuxtjs/axios] della riga 52. Non useremo questa opzione, preferendo invece la proprietà [env] della riga 88;
  • riga 90: tempo massimo di attesa per una risposta dal server di calcolo delle imposte;
  • riga 91: richiesta per il client [nuxt] — abilita l'uso dei cookie nelle comunicazioni con il server di calcolo delle imposte;
  • riga 92: l'URL di base del server di calcolo delle imposte;
  • riga 94: durata della sessione Nuxt (5 min);
  • riga 77: la navigazione del client e del server [nuxt] sarà controllata dal middleware di routing;

15.4. Il livello [UI] dell'applicazione

Image

Concederemo all'applicazione [nuxt] l'accesso all'API del server di calcolo delle imposte tramite la seguente vista:

Image

  • in [2], il menu che fornisce l'accesso all'API del server di calcolo delle imposte:
    • [Autenticazione]: corrisponde alla pagina [authentication]. Questa pagina invia una richiesta di autenticazione al server di calcolo delle imposte utilizzando le credenziali [admin, admin], che al momento sono le uniche autorizzate. Il risultato visualizzato è simile a [3];
    • [Richiesta AdminData]: corrisponde alla pagina [get-admindata]. Questa pagina richiede dati dal server di calcolo delle imposte — qui indicati come [adminData] — che consentono il calcolo delle imposte. Il risultato visualizzato è simile a [3];
    • [End Tax Session]: corrisponde alla pagina [end-session]. Questa pagina invia una richiesta PHP di fine sessione al server di calcolo delle imposte. Il server quindi annulla la sessione PHP corrente e ne inizializza una nuova, vuota;

15.5. I livelli [dao] dell'applicazione [nuxt]

Come accennato in precedenza, l'architettura dell'applicazione [nuxt] sarà la seguente:

Image

  • In [1], il server [node.js] invia le pagine [nuxt] al browser [2]. È il livello [web] [8] del server che invia queste pagine. Per inviare la pagina, il server potrebbe aver richiesto dati esterni dal server dati [3]. È il livello [DAO] [9] che effettua le richieste HTTP necessarie;
  • Ad ogni richiesta di pagina al server [Node.js] [1], il browser [2] riceve l'intera applicazione [Nuxt], che poi viene eseguita in modalità SPA. Il blocco [UI] (Interfaccia Utente) [4] mostra le pagine [Vue.js] all'utente. Le azioni dell'utente o il ciclo di vita della pagina possono innescare richieste di dati esterni dal server dati [3]. Il livello [DAO] [5] effettua quindi le richieste HTTP necessarie;

Utilizzeremo la versione 14 del server di calcolo delle imposte sviluppato nel documento |Introduzione a PHP7 attraverso esempi|. Utilizzeremo solo una parte della sua API (Application Programming Interface) JSON:

Richiesta
Risposta
1
2
3
4
5
6
[inizializzazione di una sessione JSON con il
server di calcolo delle imposte]
[viene creata una sessione PHP con il
server di calcolo delle imposte]

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

{
    "action": "init-session",
    "status": 700,
    "response": "sessione avviata con tipo [json]"
}

[autenticazione utente]
[l'autenticazione è memorizzata nella
sessione PHP]

POST main.php?action=authenticate-user
parametri inviati: user, admin

{
    "action": "authenticate-user",
    "status": 200,
    "response": "Autenticazione riuscita [admin, admin]"
}

[richiesta di dati amministrativi fiscali
]
[i dati ricevuti vengono memorizzati nella
sessione PHP]

GET main.php?action=get-admindata

{
    "action": "get-admindata",
    "status": 1000,
    "response": {
        "limits": [
            9964,
            27519,
            73779,
            156244,
            0
        ],
        "coeffR": [
            0,
            0,14,
            0,3,
            0,41,
            0,45
        ],
        "coeffN": [
            0,
            1394,96,
            5798,
            13913,69,
            20163,45
        ],
        "half-share-income-limit": 1551,
        "LimiteRedditoSingoloPerRiduzione": 21037,
        "limite-reddito-coppia-per-riduzione": 42074,
        "valore-riduzione-metà-quota": 3797,
        "limite-sconto-singolo": 1196,
        "limite-sconto-coppia": 1970,
        "soglia-fiscale-coppia-per-sconto": 2627,
        "singleTaxCeilingForDiscount": 1595,
        "MaxTenPercentDeduction": 12502,
        "scontoMinimo10%": 437
    }
}

[fine della sessione PHP con il
calcolo delle imposte]
[elimina la sessione PHP corrente e crea una
nuova sessione PHP. In questa sessione, la
sessione JSON rimane attiva, ma l'utente
non è più autenticato]

GET main.php?action=end-session

{
    "action": "end-session",
    "status": 400,
    "response": "sessione eliminata"
}

15.5.1. Il livello [DAO] del server [Nuxt]

Image

Il server [node.js] [1] utilizzerà il livello [DAO] descritto nel documento |Introduzione al framework VUE.JS attraverso esempi|. Ecco nuovamente il codice:


'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;
  • Tutti i metodi nel livello [dao] restituiscono l'oggetto inviato dal server dati [{action: ‘xx’, status: nn, response: {...}] con:
    • [action]: il nome dell'azione eseguita dal server dati;
    • [state]: indicatore numerico:
      • [initSession]: status=700 per una risposta senza errori;
      • [authenticateUser]: status=200 per una risposta senza errori;
      • [getAdminData]: status=1000 per una risposta senza errori;
      • [end-session]: status=400 per una risposta senza errori;
    • [response]: risposta associata all'indicatore numerico [status]. Può variare a seconda di questo indicatore numerico;

Esaminiamo il costruttore della classe [Dao]:


// constructeur
  constructor(axios) {
    this.axios = axios;
    // cookie de session
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
}
  • riga 2: l'oggetto [axios] passato come argomento al costruttore è fornito dal codice chiamante. È questo oggetto che effettuerà le richieste HTTP;
  • riga 5: il nome del cookie di sessione inviato dal server dati scritto in PHP;
  • riga 6: il cookie di sessione scambiato tra il livello [dao] e il server dati. Questo viene inizializzato dalla funzione [getRemoteData] nelle righe 67–113;

Per il cookie di sessione, dobbiamo considerare due livelli [dao] distinti:

  • il livello del browser;
  • il livello server;

Dovremo gestire tre cookie di sessione:

  1. quello scambiato tra il client [nuxt] e il server PHP 7;
  2. quello scambiato tra il server [nuxt] e il server PHP 7;
  3. quello scambiato tra il client [nuxt] e il server [nuxt];

Ci assicureremo che il cookie di sessione con il server PHP sia lo stesso sia per il client che per il server [nuxt]. Chiameremo questo cookie il cookie di sessione PHP. Questo cookie è quello dei casi 1 e 2. Chiameremo il cookie del caso 3 il cookie di sessione [nuxt]. Avremo quindi due sessioni:

  • una sessione PHP con il cookie di sessione PHP;
  • una sessione [nuxt] con il cookie di sessione [nuxt];

Perché utilizzare lo stesso cookie per le sessioni PHP del client e le sessioni del browser [nuxt]? Vogliamo che l'applicazione sia in grado di comunicare con il server PHP 7 indipendentemente dal fatto che si tratti del client o del server [nuxt]:

  • se un'azione A dal server [nuxt] porta il server PHP nello stato E, questo stato si riflette nella sessione PHP gestita dal server PHP;
  • utilizzando lo stesso cookie di sessione PHP del server, un'azione client B [nuxt] successiva all'azione server A [nuxt] troverebbe il server PHP nello stato E lasciato dal server [nuxt] e potrebbe quindi basarsi sul lavoro già svolto dal server [nuxt];
  • se, dopo l'azione B del client [nuxt], segue un'azione C del server [nuxt], per lo stesso motivo di prima, questa azione potrà basarsi sul lavoro svolto dall'azione B del client [nuxt];

Per consentire al browser del client [nuxt] di comunicare con il server PHP di calcolo delle imposte, useremo la versione 14 di questo server, che permette le chiamate cross-domain, cioè le chiamate da un browser al server PHP. Le chiamate dal server [nuxt] al server PHP, invece, non sono chiamate cross-domain. Questo concetto si applica solo alle chiamate effettuate da un browser.

Torniamo al codice del costruttore della precedente classe [Dao]:


// constructeur
  constructor(axios) {
    this.axios = axios;
    // cookie de session
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
}
  • Le righe 5 e 6 corrispondono al cookie di sessione PHP con il server di calcolo delle imposte;

La gestione del cookie di sessione PHP sopra indicata non è adatta al server [nuxt]: il suo livello [dao] viene istanziato ad ogni nuova richiesta effettuata al server [nuxt]. Ricordiamo che richiedere una pagina dal server [nuxt] di fatto resetta l'applicazione [nuxt]. Pertanto, quando il server [nuxt] effettua la sua prima richiesta al server dati, il cookie di sessione PHP per il livello [dao] viene inizializzato; questo valore va perso durante la successiva richiesta HTTP dallo stesso server [nuxt], poiché nel frattempo il suo livello [dao] è stato ricreato, il costruttore rieseguito e il cookie di sessione PHP reimpostato su una stringa vuota (riga 6);

Una soluzione consiste nell'utilizzare un costruttore diverso per il livello [dao] del server:


// 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'
  }
  • riga 2: questa volta, il cookie di sessione PHP verrà fornito al costruttore del livello [DAO] del server dati;

In che modo il server [nuxt] fornirà questo cookie di sessione PHP al costruttore del suo livello [dao]? Memorizzeremo il cookie di sessione PHP nel cookie di sessione [nuxt] scambiato tra il browser e il server [nuxt]. Il processo è il seguente:

  1. L'applicazione [nuxt] viene avviata;
  2. quando il server [nuxt] effettua la sua prima richiesta HTTP al server PHP, memorizza il cookie di sessione PHP ricevuto nel cookie di sessione [nuxt] che scambia con il client [nuxt];
  3. Il browser che ospita il client [nuxt] riceve questo cookie di sessione [nuxt] e quindi lo rinvia automaticamente con ogni nuova richiesta al server [nuxt];
  4. quando il server [nuxt] deve effettuare una nuova richiesta al server PHP, troverà il cookie di sessione PHP all'interno del cookie di sessione [nuxt] che il browser gli ha inviato. Lo invierà quindi al server PHP;

Esistono infatti due cookie di sessione, che non devono essere confusi:

  • il cookie di sessione [nuxt] scambiato tra il server [nuxt] e il browser del client [nuxt];
  • il cookie di sessione PHP scambiato tra il server [nuxt] e il server PHP o tra il client [nuxt] e il server PHP;

Torniamo ora al codice del metodo della classe [Dao]. Esso non include una funzione per chiudere la sessione PHP con il server di calcolo delle imposte. Aggiungeremo questa:


// 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
  }

Durante il test, abbiamo scoperto che la funzione [getRemoteData] chiamata alla riga 12 non è adatta al metodo [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;
  }
  • righe 30–43: cerchiamo il cookie [PHPSESSID=xxx]. Se trovato, viene memorizzato nella classe (riga 36);

Questo codice non è adatto al nuovo metodo [finSession] perché, all'azione [fin-session], il server PHP invia due cookie denominati [PHPSESSID]. Ecco un esempio ottenuto utilizzando un client [Postman]:

Image

  • in [1], la richiesta dal client [Postman];
  • in [3], la risposta del server PHP;
  • in [4], le intestazioni HTTP della risposta del server PHP;

Image

  • nel punto [5], il server PHP indica innanzitutto di aver cancellato la sessione PHP corrente;
  • in [6], il server PHP invia il cookie per la nuova sessione PHP;

Con il codice attuale, la funzione [getRemoteData] recupera il cookie [5], mentre è il cookie [6] che deve essere memorizzato.

Dobbiamo quindi aggiornare il codice della funzione [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
  }
  • Riga 41: Abbiamo trovato un cookie denominato [PHPSESSID]. Lo salviamo localmente;
  • riga 43: controlliamo se il cookie salvato contiene la stringa [PHPSESSID=deleted];
  • riga 46: se la risposta è no, allora abbiamo trovato il cookie corretto [PHPSESSID]. Lo memorizziamo nella classe;

Dopo la funzione [getRemoteData], il cookie di sessione PHP viene memorizzato nella classe, in [this.phpSessionCookie]. Abbiamo detto che la classe viene istanziata ad ogni nuova richiesta HTTP proveniente dal server [nuxt]. Il cookie di sessione PHP deve quindi essere estratto dalla classe. Per farlo, aggiungiamo un nuovo metodo:


// accès au cookie de la session PHP
  getPhpSessionCookie() {
    return this.phpSessionCookie
}
  • Il server [nuxt] richiede un'azione al proprio livello [dao] fornendo il cookie di sessione PHP al suo costruttore, se ne possiede uno;
  • Una volta completata l'azione, il server [nuxt] recupera il cookie di sessione PHP memorizzato dal livello [dao] utilizzando il precedente metodo [getPhpSessionCookie]. Questo cookie può essere lo stesso di quello precedente o uno diverso. Quest'ultimo caso si verifica in due occasioni:
    • quando viene eseguito il metodo [initSession] (prima non c'era alcun cookie di sessione PHP);
    • quando viene eseguito il metodo [finSession] (il server PHP modifica il cookie di sessione PHP);

Da notare una particolarità relativa al cookie di sessione PHP. Il server [nuxt] non riceve sempre questo cookie dal server PHP. Infatti, il server PHP lo invia solo una volta. Dopodiché, non lo invia più. Osservando il codice di [getRemoteData] e [getPhpSessionCookie], possiamo notare che quando il server PHP non invia un cookie di sessione, la funzione [getPhpSessionCookie] restituisce il cookie di sessione PHP fornito al costruttore. In questo modo il client [nuxt] invia sempre al server PHP l'ultimo cookie di sessione PHP che il server PHP gli ha inviato.

15.5.2. Il livello [dao] del client [nuxt]

Image

Per il client [nuxt] in esecuzione in un browser, utilizziamo il codice della classe [Dao] presente nel documento |Introduzione al framework VUE.JS attraverso esempi|:


"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;

Questo codice differisce dal livello [dao] del server [nuxt] in quanto non gestisce il cookie di sessione PHP con il server di calcolo delle imposte: se ne occupa il browser.

Come abbiamo fatto per il livello [dao] del server [nuxt], aggiungeremo un metodo [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
  }

Quando il client [nuxt] esegue questo metodo, riceve, proprio come il server [nuxt], due cookie di sessione PHP. In realtà è il browser a riceverli, e gestisce correttamente la situazione: mantiene solo il cookie della nuova sessione PHP avviata dal server di calcolo delle imposte. Quindi, la prossima volta che il client [nuxt] invia una richiesta al server PHP, il cookie di sessione PHP sarà corretto perché è il browser a inviarlo. Tuttavia, c'è un problema: il server [nuxt] non è a conoscenza del fatto che il cookie di sessione PHP è cambiato. Nelle sue comunicazioni con il server PHP, invierà quindi un cookie di sessione PHP che non esiste più, e questo causerà dei problemi. Il client [nuxt] deve notificare al server [nuxt] che il cookie di sessione PHP è cambiato e trasmetterlo al server. Sappiamo come può farlo: tramite il cookie di sessione [nuxt], il cookie scambiato tra il client e il server [nuxt]. Il client [nuxt] ha almeno due modi per recuperare il nuovo cookie di sessione PHP:

  1. richiedendolo al browser;
  2. utilizzando il metodo [getRemoteData] del server, che sa come recuperare il nuovo cookie di sessione PHP;

Useremo la seconda soluzione perché è già pronta all'uso. Il metodo [getRemoteData] del client [nuxt] diventa quindi il seguente:


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
  }

Abbiamo mantenuto solo il codice in [getRemoteData] che elabora la risposta del server PHP per recuperare il cookie di sessione PHP. Non abbiamo mantenuto il codice che includeva il cookie di sessione PHP nella richiesta al server PHP perché se ne occupa il browser che ospita il client [nuxt].

Una volta che il cookie di sessione PHP è stato ottenuto dal client [nuxt], deve essere inserito nella sessione [nuxt] in modo che il server [nuxt] possa utilizzarlo. Il livello [dao] non si occupa di questo, ma fornisce l'accesso tramite un metodo al cookie di sessione PHP che ha memorizzato:


// accès au cookie de la session PHP
  getPhpSessionCookie() {
    return this.phpSessionCookie
}

La funzione [getPhpSessionCookie] non restituisce sempre un cookie di sessione valido:

  • è importante ricordare che il livello [dao] del client [nuxt] è persistente. Viene istanziato una volta e poi rimane in memoria;
  • finché il server PHP non invia un cookie di sessione PHP al client [nuxt], la funzione [getPhpSessionCookie] del client [nuxt] restituisce un valore [undefined];
  • quando il server PHP invia un cookie di sessione PHP al client [nuxt], questo viene memorizzato in [this.phpSessionCookie] e vi rimarrà fino a quando non verrà sostituito da un nuovo cookie di sessione PHP inviato dal server PHP. La funzione [getPhpSessionCookie] del client [nuxt] restituisce quindi l'ultimo cookie di sessione PHP ricevuto;

Il livello [dao] del client [nuxt] differisce da quello del server [nuxt] solo sotto un aspetto: non invia il cookie di sessione PHP stesso, poiché se ne occupa il browser. Tuttavia, abbiamo scelto di mantenere due livelli [dao] distinti perché il ragionamento alla base delle rispettive implementazioni è diverso.

15.6. La sessione [nuxt]

Image

La sessione [nuxt] (tra il client e il server Nuxt) sarà incapsulata nel seguente oggetto [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
  • righe 5–10: la sessione ha una sola proprietà [value] con due sottoproprietà:
    • [initStoreDone], che indica se lo store è stato inizializzato o meno;
    • [store]: il valore [store.state] dello store Vuex dell'applicazione;
  • righe 12–18: il metodo [save] viene utilizzato per salvare la sessione [nuxt] in un cookie. Qui, utilizziamo la libreria [cookie-universal-nuxt] per gestire il cookie. Notare il nome del cookie di sessione [nuxt]: [nuxt-session] (riga 17);
  • righe 20–26: il metodo [reset] reimposta la sessione [nuxt];
    • riga 23: lo store Vuex viene reimpostato e poi salvato nella sessione alla riga 25;

15.7. plugin di gestione della sessione [nuxt]

Image

15.7.1. Il plugin di gestione della sessione [nuxt] per il server [nuxt]

All'avvio dell'applicazione, il server [nuxt] viene eseguito per primo. È quindi responsabile dell'inizializzazione della sessione [nuxt]. Lo script [server/plgSession] è il seguente:


/* 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)
}
  • riga 4: importa il codice della sessione [nuxt];
  • riga 11: recupera il valore del cookie di sessione [nuxt];
  • righe 12–15: se il cookie di sessione [nuxt] non esisteva, allora la sessione [nuxt] importata alla riga 4 è sufficiente. Non c'è altro da fare;
  • righe 15–19: se il cookie di sessione [nuxt] esisteva, allora alla riga 18 memorizziamo il suo valore nella sessione importata alla riga 4;
  • riga 22: la sessione è stata inizializzata o ripristinata. La rendiamo disponibile tramite la funzione [$session];

15.7.2. Il plugin di gestione della sessione [nuxt] per il client [nuxt]

Lo script [client/plgSession] è il seguente:


/* 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)
}
  • riga 4: viene importata la sessione [nuxt];
  • riga 10: recuperiamo la sessione [nuxt] corrente dal cookie [nuxt-session];
  • riga 13: restituiamo la sessione [nuxt] importata alla riga 4 tramite la funzione iniettata [$session];

15.8. Plugin per i livelli [dao]

Image

15.8.1. Il plugin del livello [dao] per il client [nuxt]

Lo script [client/plgDao] è il seguente:


/* 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]')
}
  • riga 3: viene importato il livello [dao] del client [nuxt];
  • righe 6-8: configuriamo l'oggetto [context.$axios], che effettuerà richieste HTTP per il livello [dao] del client [nuxt] utilizzando le informazioni del file [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
  }
  • riga 10: viene istanziato il livello [dao] del client [nuxt];
  • riga 12: la funzione [$dao] viene iniettata nel contesto e nelle pagine del client. Questa funzione fornisce l'accesso al livello [dao] della riga 10;

Quindi, per accedere al livello [dao] del client [nuxt] quando è in esecuzione, scriviamo:

  • [context.app.$dao()] dove il contesto è noto;
  • [this.$dao()] in una pagina [Vue.js];

15.8.2. Il plugin del livello [dao] per il server [nuxt]

Lo script [server/plgDao] è il seguente:


/* 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]')
}
  • riga 3: viene importato il livello [dao] del server [nuxt];
  • righe 6-7: l'oggetto [context.$axios] viene configurato per effettuare richieste HTTP per il livello [dao] del server [nuxt] utilizzando le informazioni del file [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
  }
  • riga 9: recupera lo store dell'applicazione [nuxt];
  • riga 10: se lo store esiste, recuperiamo il cookie di sessione PHP perché ci serve per istanziare il livello [dao] del server [nuxt];
  • riga 13: istanziare il livello [dao] del server [nuxt];
  • riga 15: la funzione [$dao] viene iniettata nel contesto e nelle pagine del server [nuxt]. Questa funzione fornisce l'accesso al livello [dao] della riga 13;

Quindi, per accedere al livello [dao] del server [nuxt] quando è in esecuzione, scriviamo:

  • [context.app.$dao()] dove il contesto è noto;
  • [this.$dao()] in una pagina [Vue.js];

15.9. Lo store Vuex

Image

Lo store [Vuex] memorizzerà tutti i dati che devono essere condivisi dai vari componenti dell'applicazione [pagine, client, server] senza che questi dati siano reattivi.


/* 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)
}

I dati memorizzati nello store sono i seguenti:

  • riga 6: [jsonSessionStarted] verrà impostato su true non appena la sessione JSON con il server PHP sarà stata inizializzata con successo, indipendentemente dal fatto che sia stata avviata dal client o dal server [nuxt]. Al termine di questa inizializzazione, il cookie di sessione per il server PHP verrà recuperato e memorizzato nella proprietà [phpSessionCookie], riga 10;
  • riga 8: [userAuthenticated] verrà impostato su true non appena l'autenticazione con il server PHP avrà esito positivo, sia che venga eseguita dal client o dal server [nuxt];
  • riga 12: [adminData] sarà il valore [adminData] ottenuto dal server PHP una volta che l'autenticazione avrà avuto esito positivo;
  • righe 18–22: l'operazione [replace] inizializza le proprietà precedenti con quelle di un oggetto passato come parametro;
  • righe 24–26: la mutazione [reset] ripristina le proprietà dello store ai loro valori iniziali;
  • righe 31–37: la funzione [nuxtServerInit] delega il proprio lavoro alla funzione [initStore];
  • righe 39–60: la funzione [initStore] ha due ruoli:
    • se lo store non è stato inizializzato, viene inizializzato e aggiunto alla sessione;
    • se lo store è già stato inizializzato, il suo valore viene recuperato dalla sessione [nuxt];
  • riga 42: viene recuperata la sessione nuxt;
  • riga 44: si verifica se lo store è stato inizializzato:
    • in caso contrario, lo store iniziale viene inserito nella sessione (riga 48);
    • quindi, alla riga 50, indichiamo che lo store è stato inizializzato;
  • righe 51–55: se lo store è stato inizializzato, lo usiamo, riga 54, per inizializzare lo store con il valore contenuto nella sessione;
  • riga 57: in tutti i casi, la sessione viene salvata nel cookie [nuxt-session], insieme allo store che contiene;

15.10. Il plugin [plgEventBus]

Image

Questo plugin mira a rendere un event bus accessibile al client [nuxt] tramite una funzione [$eventBus] iniettata nel contesto client [nuxt]. Non è necessario iniettarlo nel contesto server [nuxt] perché il server non può gestire gli eventi. Tuttavia, abbiamo già visto che iniettarlo sul lato server e poi utilizzarlo non causa un errore.


/* 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]')
}

Abbiamo già incontrato questo plugin nella sezione dei collegamenti. La funzione [$eventBus] sarà disponibile per il client tramite la seguente notazione:

  • [context.app.$eventBus()] dove il contesto è disponibile;
  • [this.$eventBus()] nelle pagine [Vue.js] del client;

15.11. I componenti dell'applicazione [Nuxt]

Image

Il componente [layout] è quello degli esempi precedenti:


<!-- 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>

Il componente [navigation] è il seguente:


<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. I layout dell'applicazione [nuxt]

Image

15.12.1. [default]

Il layout [default] è quello utilizzato per l'esempio [nuxt-11] nel paragrafo collegato:


<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>
  • righe 10–14: visualizza il messaggio che indica che il client [nuxt] è in attesa del completamento di un'operazione asincrona;
  • righe 15–18: visualizzano eventuali messaggi di errore provenienti da un'operazione asincrona;
  • riga 37: la funzione [created] della pagina [default] viene eseguita prima della funzione [mounted] delle pagine;
  • riga 39: se l'esecutore è il client [nuxt], la pagina [default] ascolta
    • [loading], che segnala l'inizio o la fine di un'attesa. Viene quindi eseguita la funzione [mShowLoading];
    • [errorLoading], che segnala che deve essere visualizzato un messaggio di errore. Viene quindi eseguita la funzione [mShowErrorLoading];
  • le pagine [nuxt]:
    • visualizzano il messaggio di caricamento emettendo l'evento [‘loading’, true] sul bus degli eventi;
    • nascondono il messaggio di caricamento emettendo l'evento [‘loading’, false] sul bus degli eventi;
    • visualizzano un messaggio di errore emettendo l'evento [‘errorLoading’, true] sul bus degli eventi;
    • nascondono il messaggio di errore emettendo l'evento [‘errorLoading’, false] sul bus degli eventi;

15.12.2. [error]

Il layout [error] visualizza un messaggio di errore di sistema (non gestito dallo sviluppatore):


<!-- 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. La pagina [index] fornita dal server [nuxt]

Image

La pagina [index.vue] è unica in quanto è accessibile solo tramite il server [nuxt]. Non viene fornito alcun link all'utente per accedervi tramite il client [nuxt]. Il suo codice è il seguente:


<!-- 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>
  • riga 7: la pagina visualizza il risultato [result] di una richiesta asincrona (righe 46 e 51);
  • riga 31: l'operazione asincrona consiste nell'aprire una sessione JSON con il server di calcolo delle imposte;
  • riga 25: sappiamo che quando la pagina viene richiesta direttamente dal server [nuxt], la funzione [asyncData] viene eseguita solo dal server e non dal client [nuxt], che viene eseguito una volta che il browser ha ricevuto la risposta dal server [nuxt];
  • riga 30: recuperiamo il livello [dao] dal contesto del server [nuxt];
  • riga 35: se il server non ha ancora effettuato una richiesta al server di calcolo delle imposte, riceve il suo primo cookie di sessione PHP; altrimenti, riceve l'ultimo cookie di sessione PHP che ha ricevuto (vedi il codice per il livello [dao] del server [nuxt] nella sezione collegata);
  • riga 37: questo cookie di sessione PHP viene memorizzato nello store;
  • righe 39–42: verifichiamo se l'operazione ha avuto esito positivo. In caso contrario, viene generata un'eccezione, che verrà intercettata dal [catch] alla riga 47;
  • riga 44: annotiamo nello store che la sessione JSON con il server PHP è iniziata;
  • riga 46: il risultato [result] viene restituito e visualizzato alla riga 7;
  • righe 47–54: gestiamo eventuali eccezioni. Queste possono essere di due tipi:
    • l'operazione HTTP alla riga 31 non è andata a buon fine a causa di un errore di comunicazione tra il server [nuxt] e il server PHP;
    • l'operazione HTTP alla riga 31 è riuscita ma il risultato ricevuto ha segnalato un errore (righe 39–42);
  • riga 51: notiamo che la sessione JSON con il server PHP non è stata avviata;
  • riga 53: il risultato [result] viene restituito e visualizzato alla riga 7. Inoltre, vengono impostate le proprietà [showErrorLoading] e [errorLoadingMessage], che il client [nuxt] utilizzerà per visualizzare un messaggio di errore quando riceve la pagina inviata dal server [nuxt] (righe 72–79);
  • righe 54–60: codice eseguito in tutti i casi (successo o fallimento);
  • riga 56: recuperiamo la sessione [nuxt] dal contesto del server [nuxt];
  • riga 57: la salviamo;
  • righe 63–68: una volta terminata la funzione [asyncData], il server [nuxt] esegue le funzioni [beforeCreate] e [create];

Nota: l'esecuzione della pagina [index] da parte del server [nuxt] potrebbe fallire, ad esempio, se il server di calcolo delle imposte non è in esecuzione al momento del lancio dell'applicazione [nuxt]:

Image

In questo caso, l'unica soluzione è avviare il server di calcolo delle imposte e poi l'applicazione [nuxt] stessa, poiché il menu di navigazione non offre un'opzione per avviare una sessione JSON con il server di calcolo delle imposte;

15.14. La pagina [index] eseguita dal client [nuxt]

La pagina [index] viene eseguita dal client [nuxt] solo dopo che il server [nuxt] l'ha inviata al client. Il server ha inviato le informazioni [result] e, se del caso, [showErrorLoading] e [errorLoadingMessage].

Sappiamo che la funzione [asyncData] non verrà eseguita. Rimangono quindi le funzioni del ciclo di vita, in particolare la funzione [mounted]:


mounted() {
    console.log('[index mounted]')
    // client seulement
    if (this.showErrorLoading) {
      console.log('[index mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
}
  • Il client [nuxt] include automaticamente l'elemento [result] e, se applicabile, gli elementi [showErrorLoading] e [errorLoadingMessage] inviati dal server [nuxt] nelle proprietà della pagina:
  • la proprietà [result] viene visualizzata alla riga 7;
  • Le proprietà [showErrorLoading, errorLoadingMessage] sono utilizzate dal metodo [mounted]: alla riga 4, viene controllata la proprietà [showErrorLoading]. Se è vera, alla riga 6, viene utilizzato il bus di eventi [nuxt] del client per segnalare che c'è un messaggio di errore da visualizzare;
  • l'evento [errorLoading] attivato alla riga 6 viene intercettato dalla pagina [layouts/default] descritta nella sezione collegata;

15.15. La pagina [authentication] eseguita dal server [nuxt]

La pagina [authentication] è responsabile dell'autenticazione di un utente con il server di calcolo delle imposte. Il suo codice è il seguente:


<!-- 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>
  • riga 7: la pagina visualizza il risultato [result] della richiesta asincrona [asyncData] delle righe 25–65;
  • righe 28–33: il server non esegue queste righe destinate al client [nuxt];
  • riga 36: il livello [dao] viene recuperato dal server [nuxt];
  • riga 37: effettuiamo l'autenticazione con il server di calcolo delle imposte utilizzando le credenziali di prova [admin, admin], che sono le uniche accettate dal server di calcolo delle imposte;
  • riga 41: l'operazione di autenticazione ha esito positivo solo se lo stato della risposta è 200;
  • riga 43: impostiamo la proprietà [userAuthenticated] nello store;
  • righe 44–46: lo store viene salvato nella sessione [nuxt];
  • righe 48–51: se l'autenticazione non va a buon fine, generiamo un'eccezione con il messaggio di errore inviato dal server di calcolo delle imposte;
  • altrimenti, alla riga 53, restituisce un risultato di successo che verrà visualizzato alla riga 7;
  • righe 54–57: in caso di errore, vengono impostate tre proprietà della pagina [result, showErrorLoading, errorLoadingMessage]. La proprietà [result] verrà visualizzata alla riga 7. Le tre proprietà saranno inviate al client [nuxt];
  • righe 60–63: non vengono eseguite dal server [nuxt];
  • una volta che [asyncData] ha restituito il suo risultato, questo viene visualizzato alla riga 7. Quindi vengono eseguiti i metodi [beforeCreate] (righe 67–69) e [created] (righe 70–72);
  • il gioco è fatto;

Nota: la pagina [authentification] potrebbe non essere eseguita sul server [nuxt], ad esempio se la sessione JSON con il server di calcolo delle imposte non è stata inizializzata. Ciò può essere fatto come segue:

  • elimina il cookie di sessione PHP dal tuo browser (per ricominciare da zero):

Image

  • avvia l'applicazione [nuxt] mentre il server di calcolo non è stato avviato: otterrai un errore;
  • avvia il server di calcolo delle imposte;
  • inserisci l'URL [/authentication] direttamente nella barra degli indirizzi del browser:

Image

In questo caso, l'unica soluzione è ricaricare nuovamente la pagina [index].

15.16. La pagina [authentication] eseguita dal client [nuxt]

Diamo un'altra occhiata al codice della pagina:


<!-- 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>

Esistono due scenari in cui la pagina [authentication] viene eseguita dal client [nuxt]:

  1. il client [nuxt] viene eseguito dopo che il server [nuxt] ha inviato la pagina [authentication] al browser del client [nuxt];
  2. il client [nuxt] viene avviato perché l'utente ha cliccato sul link [Autenticazione] nel menu di navigazione:

Image

Esaminiamo innanzitutto il primo scenario. In questo caso, il client [nuxt] non esegue la funzione [asyncData]. Incorpora l'elemento [result] e, se applicabile, gli elementi [showErrorLoading] e [errorLoadingMessage] inviati dal server [nuxt] nelle proprietà della pagina:

  • la proprietà [result] viene visualizzata alla riga 7;
  • le proprietà [showErrorLoading, errorLoadingMessage] vengono utilizzate dal metodo [mounted]: alla riga 79 viene verificata la proprietà [showErrorLoading]. Se è vera, alla riga 81 viene utilizzato l'event bus del client [nuxt] per segnalare che c'è un messaggio di errore da visualizzare;

Il meccanismo per la visualizzazione del messaggio di errore è stato spiegato per la pagina [index] nella sezione dei link.

Il caso 2 si verifica quando il client [nuxt] viene eseguito dopo che l'utente ha cliccato sul link [Authentication]. In questo caso, il client [nuxt] viene eseguito in modo indipendente e non dopo il server [nuxt]. Viene quindi eseguita la funzione [asyncData]. Forniamo solo i dettagli che differiscono dalle spiegazioni fornite per la pagina eseguita dal server [nuxt]:

  • righe 28–33: il client [nuxt] richiede che venga visualizzato il messaggio di caricamento e che venga cancellato qualsiasi messaggio di errore visualizzato in precedenza;
  • riga 36: ora è il livello [dao] del client [nuxt] che viene recuperato qui;
  • righe 60–63: il client [nuxt] richiede che il messaggio di caricamento venga rimosso;
  • Una volta terminato [asyncData], il ciclo di vita della pagina proseguirà. Verrà eseguita la funzione [mounted] nelle righe 76–83. Se si è verificato un errore, verrà quindi visualizzato il messaggio di errore;

Nota: per generare un errore, segui la procedura spiegata per il server [nuxt] alla fine della sezione dei link, ma invece di richiedere la pagina [authentication] digitando il suo URL nella barra degli indirizzi, usa il link [Authentication] nel menu di navigazione. Questo farà eseguire il client [nuxt].

15.17. La pagina [get-admindata]

Il codice per la pagina [get-admindata] è il seguente:


<!-- 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>

Questa pagina è molto simile alla pagina [authentication]. Le spiegazioni sono le stesse sia che venga eseguita dal server [nuxt] sia dal client [nuxt]. Si noti, tuttavia, che la riga 7 non visualizza il successo/fallimento come prima, ma piuttosto il valore dei dati ricevuti dal server di calcolo delle imposte (riga 52):

Image

Il risultato sopra riportato si ottiene utilizzando sia il server che il client [nuxt]. Per generare un errore, richiedere la pagina [get-admindata] — tramite il server o il client [nuxt] — senza essere autenticati:

Image

15.18. La pagina [fin-session]

Il codice della pagina è il seguente:


<!-- 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>

Il codice è molto simile a quello delle pagine precedenti e le spiegazioni sono le stesse. C'è solo un punto da notare: l'operazione asincrona alla riga 38 fa sì che il server di calcolo delle imposte invii un nuovo cookie di sessione PHP. Le istruzioni per la gestione di questo cookie variano a seconda che sia il server o il client [nuxt] a eseguire questo codice.

Cominciamo con il server [nuxt]:

  • riga 37: questo è il livello [dao] del server [nuxt] che viene istanziato. Ricordiamo il codice del suo costruttore:

// 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'
}

Alla riga 1, vediamo che il costruttore richiede il cookie di sessione PHP corrente, ovvero l'ultimo ricevuto, sia dal server che dal client [nuxt];

  • riga 52: il server [nuxt] recupera il cookie per la nuova sessione PHP o il vecchio cookie se l'operazione di terminazione della sessione non è andata a buon fine;
  • riga 54: il cookie di sessione PHP viene inserito nello store e poi salvato nella sessione [nuxt] alle righe 56–57;
  • dopo il server, è il client [nuxt] che esegue la pagina [end-session] con i dati inviati dal server. Sappiamo che non eseguirà la funzione [asyncData];
  • In definitiva, dopo che il server e il client [nuxt] hanno terminato il loro lavoro, sappiamo che il cookie PHP necessario per la comunicazione con il server di calcolo delle imposte si trova nella sessione [nuxt];

Il fatto che il cookie PHP si trovi nella sessione [nuxt] è sufficiente per il server, perché è lì che il suo livello [dao] lo recupererà. Nel plugin [server/plgDao] che inizializza il livello [dao] del server, abbiamo scritto:


/* 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]')
}
  • Riga 13: il livello [dao] del server [nuxt] viene istanziato con il cookie di sessione PHP prelevato dalla sessione [nuxt], righe 9–10;

Per il client [nuxt], la situazione è diversa. Non è il client a inviare il cookie, bensì il browser che lo esegue. Tuttavia, questo browser non conosce il cookie della nuova sessione PHP ricevuta dal server [nuxt]. Se utilizziamo i link nel menu di navigazione [3]:

Image

Il server di calcolo delle imposte riceverà dal browser un cookie di sessione PHP non aggiornato e risponderà che non esiste alcuna sessione JSON associata a questo cookie. Dobbiamo trovare un modo per trasmettere il nuovo cookie di sessione PHP al browser.

Possiamo utilizzare un middleware di routing per farlo:

Image

Lo script [client/routing] è il middleware di routing dichiarato nel file [nuxt.config]:


// router
  router: {
    // application URL root
    base: '/nuxt-12/',
    // routing middleware
    middleware: ['routing']
},

Lo script [middleware/routing] è il seguente:


/* 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)
  }
}
  • Righe 9–12: Indirizziamo il client solo utilizzando una funzione importata alla riga 4;

Lo script [middleware/client/routing] è il seguente:


/* 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
  }
}

Torniamo alla situazione immediatamente successiva all'esecuzione della pagina [fin-session] da parte del server [nuxt]:

Image

Se clicchi su uno dei link nel menu [3], il client [nuxt] prende il controllo. Poiché ci sarà un cambio di pagina, verrà eseguito lo script di routing del client:

  • riga 13: il cookie di sessione PHP viene trovato nell'archivio dell'applicazione [nuxt];
  • riga 14: se non è vuoto, viene inviato al browser (riga 16). Da questo momento in poi, il browser del client [nuxt] dispone del cookie di sessione PHP corretto;

Lo script [client/routing] viene eseguito ogni volta che il client [nuxt] cambia pagina. Il codice dello script funziona indipendentemente dalla pagina di destinazione: in sostanza, il più delle volte invia al browser un cookie di sessione PHP che già possiede, tranne in due casi:

  • subito dopo l'avvio dell'applicazione, il server [nuxt] esegue la pagina [index] e riceve un primo cookie di sessione PHP che il browser del client [nuxt] non possiede;
  • quando il server [nuxt] esegue la pagina [end-session] come appena spiegato;

Ora esaminiamo il caso in cui la pagina [end-session] viene eseguita solo dal client [nuxt], perché l'utente ha cliccato sul suo link nel menu di navigazione. Ora è il client [nuxt] che esegue la funzione [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)
      }
    }
  • Riga 3: Questo è il livello [dao] del client [nuxt] che viene recuperato qui;
  • Riga 18: Il cookie di sessione PHP recuperato dal livello [dao] del client [nuxt] viene memorizzato, inserito nello store (riga 20) e quindi salvato nella sessione [nuxt] (righe 22–23);
  • Da quel momento in poi, tutto funziona correttamente perché sappiamo che il livello [dao] del server [nuxt] recupererà il cookie di sessione PHP dalla sessione [nuxt];

15.19. Esecuzione

Per eseguire questo esempio, è necessario innanzitutto eliminare il cookie di sessione [nuxt] e il cookie PHP dal browser che esegue il client [nuxt] per ricominciare da zero. Di seguito è riportato un esempio che utilizza il browser Chrome:

Image

15.20. Conclusione

Questo esempio era particolarmente complesso. Ha riunito le conoscenze acquisite negli esempi precedenti: persistenza dello store in una sessione [nuxt], plugin di iniezione di funzioni, middleware di routing e gestione degli errori per le operazioni asincrone. La complessità è stata accentuata dal fatto che volevamo che l'utente potesse utilizzare sia i link del menu di navigazione sia digitare manualmente gli URL senza compromettere il funzionamento dell'applicazione. Per ottenere questo risultato, abbiamo dovuto esaminare il comportamento di ciascuna pagina a seconda che fosse eseguita dal client o dal server [nuxt].

Questa coerenza nel comportamento del client e del server [Nuxt] non è essenziale. Consideriamo lo scenario comune in cui:

  • la prima pagina viene servita dal server [nuxt];
  • tutte le pagine successive sono servite dal client [nuxt], che opera quindi in modalità [SPA];

Tuttavia, anche in questo caso, è necessario verificare come tutte le pagine vengono visualizzate quando vengono eseguite dal server [nuxt], poiché questo è ciò che i motori di ricerca vedranno quando le richiederanno.