Skip to content

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

15.1. Présentation

In questo nuovo esempio, scopriremo come, nelle funzioni [asyncData], sia possibile effettuare richieste HTTP utilizzando la libreria [axios]. Inoltre, utilizzeremo nozioni già acquisite:

  • l’utilizzo dei plugin dell’esempio [nuxt-06]:
  • la persistenza dello store in un cookie di sessione dell’esempio [nuxt-06];
  • il controllo della navigazione con i middleware dell’esempio [nuxt-09];
  • la gestione degli errori dell'esempio [nuxt-11];

L’architettura dell’esempio sarà la seguente:

Image

  • l’applicazione [nuxt] sarà memorizzata sul server [node.js] [3], scaricata dal browser [1] che la eseguirà successivamente;
  • sia il client [nuxt] [1] che il server [nuxt] [3] invieranno richieste HTTP al server dati [2]. Questo server sarà il server di calcolo delle imposte sviluppato nella sezione PHP 7. Utilizzeremo la sua ultima versione, la versione 14, con le richieste CORS autorizzate;

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 al server di 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 verrà quindi eseguita in modalità SPA. Il blocco [UI] (Interfaccia utente) [4] presenta all’utente le pagine [vue.js]. Le azioni dell’utente o il ciclo di vita naturale delle pagine possono provocare richieste di dati esterni al server di dati [3]. È quindi il livello [DAO] [5] che effettua le richieste HTTP necessarie;

15.2. Struttura ad albero del progetto

Image

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

Il progetto sarà controllato dal file [nuxt.config.js] seguente:


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) { }
  },
  // directory del codice sorgente
  srcDir: 'nuxt-12',
  // router
  router: {
    // radice dell'applicazione URL
    base: '/nuxt-12/',
    // middleware di routing
    middleware: ['routing']
  },
  // server
  server: {
    // porta di servizio, 3000 per impostazione predefinita
    port: 81,
    // indirizzi di rete in ascolto, per impostazione predefinita localhost: 127.0.0.1
    // 0.0.0.0 = tutti gli indirizzi di rete del computer
    host: 'localhost'
  },
  // ambiente
  env: {
    // configurazione di Axios
    timeout: 2000,
    withCredentials: true,
    baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
    // configurazione del cookie di sessione [nuxt]
    maxAge: 60 * 5
  }
}
  • riga 22: gestiamo autonomamente l'avviso di attesa della fine di un'azione asincrona;
  • riga 31: utilizzeremo vari plugin specializzati o per il client o per il server, ma non per entrambi contemporaneamente;
  • riga 52: il modulo [axios] è integrato in [nuxt]. Ciò comporterà che l’oggetto [axios], che effettuerà le richieste HTTP dell’applicazione [nuxt] al server PHP per il calcolo delle imposte, sarà disponibile in [context.$axios];
  • riga 54: il modulo [cookie-universal-nuxt] ci consentirà 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 utilizzeremo questa opzione, preferendo invece la proprietà [env] della riga 88;
  • riga 90: tempo massimo di attesa per la risposta del server di calcolo delle imposte;
  • riga 91: necessaria per il client [nuxt] - autorizza l'uso dei cookie negli scambi con il server di calcolo delle imposte;
  • riga 92: URL di base del server di calcolo delle imposte;
  • riga 94: durata della sessione Nuxt (5 minuti);
  • riga 77: la navigazione tra client e server [nuxt] sarà controllata da un middleware di routing;

15.4. Il livello [UI] dell’applicazione

Image

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

Image

  • in [2], il menu che consente di accedere a API del server di calcolo delle imposte:
    • [Authentification]: corrisponde alla pagina [authentification]. Questa pagina invia una richiesta di autenticazione al server di calcolo delle imposte con le credenziali [admin, admin], che al momento sono le uniche autorizzate. Il risultato visualizzato è analogo a quello di [3];
    • [Requête AdminData]: corrisponde alla pagina [get-admindata]. Questa pagina richiede al server di calcolo delle imposte i dati, qui denominati [adminData], che consentono il calcolo dell’imposta. Il risultato visualizzato è analogo a [3];
    • [Fin session impôt]: corrisponde alla pagina [fin-session]. Questa pagina invia una richiesta di chiusura della sessione PHP al server di calcolo dell’imposta. Il server annulla quindi la sessione corrente PHP e ne inizializza una nuova vuota;

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

Come indicato 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 fornisce queste pagine. Per fornire la pagina, il server potrebbe aver richiesto dati esterni al server di 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 verrà quindi eseguita in modalità SPA. Il blocco [UI] (Interfaccia utente) [4] presenta all’utente le pagine [vue.js]. Le azioni di quest’ultimo o il ciclo di vita delle pagine possono provocare richieste di dati esterni al server di dati [3]. È il livello [DAO] [5] che effettua quindi le richieste HTTP necessarie;

Utilizzeremo la versione 14 del server di calcolo delle imposte sviluppato nel documento |Introduzione al linguaggio PHP7 attraverso un esempio|. Utilizzeremo solo una parte della sua API (Application Programming Interface) jSON:

Richiesta
Risposta
1
2
3
4
5
6
[initialisation d’une session jSON avec le
serveur de calcul de l’impôt]
[une session PHP est créée avec le serveur de
calcul de l’impôt]

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

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

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

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

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

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

GET main.php?action=get-admindata

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

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

GET main.php?action=fin-session

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

15.5.1. 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 un esempio|. Ne riportiamo qui il codice:


'use strict';

// importazioni
import qs from 'qs'

class Dao {

  // costruttore
  constructor(axios) {
    this.axios = axios;
    // cookie di sessione
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
  }

  // inizializzazione della sessione
  async  initSession() {
    // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      // parametri di URL
      params: {
        action: 'init-session',
        type: 'json'
      }
    };
    // esecuzione della query HTTP
    return await this.getRemoteData(options);
  }

  async  authentifierUtilisateur(user, password) {
    // opzioni della richiesta HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        'Content-type': 'application/x-www-form-urlencoded',
      },
      // corpo del POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      // parametri dell'URL
      params: {
        action: 'authentifier-utilisateur'
      }
    };
    // esecuzione della richiesta HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // opzioni della richiesta HHTP [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      // parametri della query URL
      params: {
        action: 'get-admindata'
      }
    };
    // esecuzione della query HTTP
    const data = await this.getRemoteData(options);
    // risultato
    return data;
  }

  async  getRemoteData(options) {
    // per il cookie di sessione
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    // esecuzione della richiesta HTTP
    let response;
    try {
      // richiesta asincrona
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // il parametro [error] è un'istanza di eccezione - può assumere diverse forme
      if (error.response) {
        // la risposta del server è contenuta in [error.response]
        response = error.response;
      } else {
        // l'errore viene generato nuovamente
        throw error;
      }
    }
    // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // si recupera il cookie di sessione, se presente
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie è un array
      // si cerca il cookie di sessione in questo array
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        // si cerca il cookie di sessione
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // si memorizza il cookie di sessione
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // trovato
          trouvé = true;
        } else {
          // elemento successivo
          i++;
        }
      }
    }
    // la risposta del server si trova in [response.data]
    return response.data;
  }
}

// esportazione della classe
export default Dao;
  • tutti i metodi del livello [dao] restituiscono l’oggetto inviato dal server di dati [{action : ‘xx’, état : nn, réponse : {...}] con:
    • [action]: il nome dell’azione eseguita dal server dati;
    • [état]: indicatore numerico:
      • [initSession]: stato=700 per una risposta senza errori;
      • [authentifierUtilisateur]: stato=200 per una risposta senza errori;
      • [getAdminData]: stato=1000 per una risposta senza errori;
      • [fin-session]: stato=400 per una risposta senza errori;
    • [réponse]: risposta associata all'indicatore numerico [état]. Può variare in base a tale indicatore numerico;

Esaminiamo il costruttore della classe [Dao]:


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

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

  • quello del browser;
  • quello del 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];

Faremo in modo che il cookie di sessione con il server PHP sia lo stesso sia per il client che per il server [nuxt]. Chiameremo questo cookie «cookie di sessione PHP». Questo cookie è quello dei casi 1 e 2. Chiameremo «cookie di sessione [nuxt]» il cookie del caso 3. 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 del browser [nuxt]? Vogliamo che l’applicazione possa comunicare con il server PHP indipendentemente dal fatto che si tratti del client o del server [nuxt]:

  • se un’azione A del server [nuxt] porta il server PHP in uno stato E, tale stato si riflette nella sessione PHP gestita dal server PHP;
  • utilizzando lo stesso cookie di sessione PHP del server, un’azione B del client [nuxt] che seguisse l’azione A del server [nuxt] troverebbe il server PHP nellostato E lasciato dal server [nuxt] e potrebbe quindi avvalersi del 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, tale azione potrà avvalersi del lavoro svolto dall’azione B del client [nuxt];

Affinché il browser del cliente [nuxt] possa comunicare con il server PHP per il calcolo dell’imposta, utilizzeremo la versione 14 di tale server, che consente le chiamate tra domini, ovvero quelle da un browser al server PHP. Le chiamate dal server [nuxt] al server PHP non sono invece chiamate interdominio. Questo concetto esiste solo per le chiamate effettuate da un browser.

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


// costruttore
  constructor(axios) {
    this.axios = axios;
    // cookie di sessione
    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 è adeguata per il server [nuxt]: il suo livello [dao] viene istanziato ad ogni nuova richiesta effettuata al server [nuxt]. Si ricorda infatti che richiedere una pagina al server [nuxt] equivale a reinizializzare l’applicazione [nuxt]. Pertanto, quando al termine della prima richiesta effettuata al server di dati dal server [nuxt], il cookie di sessione PHP del livello [dao] viene inizializzato, tale valore va perso durante la successiva richiesta HTTP dello stesso server [nuxt], poiché nel frattempo il suo livello [dao] è stato ricreato, il costruttore è stato rieseguito e il cookie di sessione PHP è stato reinizializzato con la stringa vuota (riga 6);

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


// costruttore
  constructor(axios, phpSessionCookie) {
    // libreria axios
    this.axios = axios
    // valore del cookie di sessione
    this.phpSessionCookie = phpSessionCookie
    // nome del cookie di sessione del server 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] potrà fornire questo cookie di sessione PHP al costruttore del proprio 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. viene avviata l’applicazione [nuxt];
  2. quando il server [nuxt] effettua la sua prima richiesta HTTP al server PHP, memorizza il cookie di sessione PHP che ha 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 lo rinvia quindi sistematicamente ad ogni nuova richiesta al server [nuxt];
  4. quando il server [nuxt] dovrà effettuare una nuova richiesta al server PHP, ritroverà il cookie di sessione PHP all’interno del cookie di sessione [nuxt] che il browser gli avrà inviato. Lo invierà quindi al server PHP;

Esistono effettivamente due cookie di sessione e non bisogna confonderli:

  • il cookie di sessione [nuxt] scambiato tra il server [nuxt] e il browser del cliente [nuxt];
  • il cookie di sessione PHP scambiato tra il server [nuxt] e il server PHP oppure 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. Aggiungiamo questa:


// fine della sessione di calcolo delle imposte
  async finSession() {
    // opzioni della richiesta HHTP  [get /main.php?action=fin-session]
    const options = {
      method: 'GET',
      // parametri di URL
      params: {
        action: 'fin-session'
      }
    }
    // esecuzione della richiesta HTTP
    const data = await this.getRemoteData(options)
    // risultato
    return data
  }

Durante i test, si scopre che la funzione [getRemoteData] chiamata alla riga 12 non è adatta al metodo [finSession]:


async  getRemoteData(options) {
    // per il cookie di sessione
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    // esecuzione della richiesta HTTP
    let response;
    try {
      // richiesta asincrona
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // il parametro [error] è un'istanza di eccezione - può assumere diverse forme
      if (error.response) {
        // la risposta del server è contenuta in [error.response]
        response = error.response;
      } else {
        // l'errore viene generato nuovamente
        throw error;
      }
    }
    // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // si recupera il cookie di sessione, se presente
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie è un array
      // si cerca il cookie di sessione in questo array
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        // si cerca il cookie di sessione
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // si memorizza il cookie di sessione
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // trovato
          trouvé = true;
        } else {
          // elemento successivo
          i++;
        }
      }
    }
    // la risposta del server è in [response.data]
    return response.data;
  }
  • righe 30-43: si cerca il cookie [PHPSESSID=xxx]. Se lo si trova, viene memorizzato nella classe (riga 36);

Questo codice non è compatibile con il nuovo metodo [finSession] poiché, nell’azione [fin-session], il server PHP invia due cookie con il nome [PHPSESSID]. Ecco un esempio ottenuto con un client [Postman]:

Image

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

Image

  • in [5], il server PHP indica innanzitutto di aver eliminato la sessione corrente PHP;
  • in [6], il server PHP invia il cookie della nuova sessione PHP;

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

È quindi necessario modificare il codice della funzione [getRemoteData]:


async getRemoteData(options) {
    // esiste un cookie di sessione PHP?
    if (this.phpSessionCookie) {
      // Ci sono delle intestazioni?
      if (!options.headers) {
        // si crea un oggetto vuoto
        options.headers = {}
      }
      // intestazione del cookie di sessione PHP
      options.headers.Cookie = this.phpSessionCookie
    }
    // esecuzione della richiesta HTTP
    let response
    try {
      // richiesta asincrona
      response = await this.axios.request('main.php', options)
    } catch (error) {
      // il parametro [error] è un'istanza di eccezione - può assumere diverse forme
      if (error.response) {
        // la risposta del server si trova in [error.response]
        response = error.response
      } else {
        // l'errore viene generato nuovamente
        throw error
      }
    }
    // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // si cerca il cookie di sessione PHP tra i cookie ricevuti
    // tutti i cookie ricevuti
    const cookies = response.headers['set-cookie']
    if (cookies) {
      // i cookie sono un array
      // si cerca il cookie di sessione PHP in questo array
      let trouvé = false
      let i = 0
      while (!trouvé && i < cookies.length) {
        // si cerca il cookie di sessione PHP
        const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
        if (results) {
          // si memorizza il cookie di sessione PHP
          const phpSessionCookie = results[1]
          // è presente al suo interno la stringa [deleted]?
          const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
          if (!results2) {
            // si ha il cookie di sessione corretto PHP
            this.phpSessionCookie = phpSessionCookie
            // è stato trovato
            trouvé = true
          } else {
            // elemento successivo
            i++
          }
        } else {
          // elemento successivo
          i++
        }
      }
    }
    // la risposta del server è in [response.data]
    return response.data
  }
  • riga 41: è stato trovato un cookie con il nome [PHPSESSID]. Lo si memorizza localmente;
  • riga 43: si verifica se nel cookie salvato è presente la stringa [PHPSESSID=deleted];
  • riga 46: se la risposta è no, significa che è stato trovato il cookie corretto [PHPSESSID]. Lo si memorizza 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 dal server [nuxt]. Il cookie di sessione PHP deve quindi essere estratto dalla classe. A tal fine, si aggiunge un nuovo metodo alla classe:


// accesso al cookie di sessione 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 eseguita l’azione, il server [nuxt] recupera il cookie di sessione PHP memorizzato dal livello [dao] utilizzando il metodo [getPhpSessionCookie] precedente. Questo cookie può essere lo stesso del precedente oppure un altro. Quest’ultimo caso si verifica in due occasioni:
    • durante l’esecuzione del metodo [initSession] (in precedenza non era presente alcun cookie di sessione PHP);
    • durante l’esecuzione del 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, quest’ultimo lo invia solo una volta. Successivamente non lo invia più. Osservando il codice di [getRemoteData] e quello di [getPhpSessionCookie], si noterà che quando il server PHP non invia alcun cookie di sessione, la funzione [getPhpSessionCookie] restituisce il cookie di sessione PHP fornito al costruttore. È così che il server invia sempre al server PHP l’ultimo cookie di sessione PHP che quest’ultimo gli ha inviato.

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

Image

Per il client [nuxt] in esecuzione in un browser, si riprende il codice della classe [Dao] dal documento |Introduzione al framework VUE.JS con un esempio|:


"use strict";

// importazioni
import qs from "qs";

class Dao {
  // costruttore
  constructor(axios) {
    this.axios = axios;
  }

  // inizializzazione sessione
  async initSession() {
    // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      // parametri di URL
      params: {
        action: "init-session",
        type: "json"
      }
    };
    // esecuzione della query HTTP
    return await this.getRemoteData(options);
  }

  async authentifierUtilisateur(user, password) {
    // opzioni della query HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        "Content-type": "application/x-www-form-urlencoded"
      },
      // corpo del POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      // parametri di URL
      params: {
        action: "authentifier-utilisateur"
      }
    };
    // esecuzione della query HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // opzioni della richiesta HHTP  [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      // parametri di URL
      params: {
        action: "get-admindata"
      }
    };
    // esecuzione della query HTTP
    const data = await this.getRemoteData(options);
    // risultato
    return data;
  }

  async getRemoteData(options) {
    // esecuzione della richiesta HTTP
    let response;
    try {
      // richiesta asincrona
      response = await this.axios.request("main.php", options);
    } catch (error) {
      // il parametro [error] è un'istanza di eccezione - può assumere diverse forme
      if (error.response) {
        // la risposta del server è contenuta in [error.response]
        response = error.response;
      } else {
        // si ripete l'errore
        throw error;
      }
    }
    // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // la risposta del server si trova in [response.data]
    return response.data;
  }
}

// esportazione della classe
export default Dao;

Questo codice si distingue dal livello [dao] del server [nuxt] in quanto non gestisce il cookie di sessione PHP con il server di calcolo delle imposte: è il browser a farlo.

Come abbiamo fatto per il livello [dao] del server [nuxt], aggiungeremo un metodo [finSession]:


// fine della sessione di calcolo dell'imposta
  async finSession() {
    // opzioni della richiesta HHTP  [get /main.php?action=fin-session]
    const options = {
      method: 'GET',
      // parametri di URL
      params: {
        action: 'fin-session'
      }
    }
    // esecuzione della query HTTP
    const data = await this.getRemoteData(options)
    // risultato
    return data
  }

Quando il client [nuxt] esegue questo metodo, riceve, come il server [nuxt], due cookie di sessione PHP. In realtà è il browser a riceverli e gestisce correttamente la situazione: conserva solo il cookie della nuova sessione PHP avviata dal server di calcolo delle imposte. Pertanto, alla successiva richiesta del client [nuxt] al server PHP, il cookie di sessione PHP sarà corretto, poiché è il browser a inviarlo. C'è tuttavia un problema: il server [nuxt] non è a conoscenza del fatto che il cookie di sessione PHP sia cambiato. Nelle sue comunicazioni con il server PHP, invierà quindi un cookie di sessione PHP che non esiste più e si verificheranno dei problemi. Il client [nuxt] dovrebbe avvisare il server [nuxt] che il cookie di sessione PHP è cambiato e trasmetterglielo. 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;

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


async getRemoteData(options) {
    // esecuzione della richiesta HTTP
    let response
    try {
      // richiesta asincrona
      response = await this.axios.request('main.php', options)
    } catch (error) {
      // il parametro [error] è un'istanza di eccezione - può assumere diverse forme
      if (error.response) {
        // la risposta del server si trova in [error.response]
        response = error.response
      } else {
        // l'errore viene generato nuovamente
        throw error
      }
    }
    // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // si cerca il cookie di sessione PHP tra i cookie ricevuti
    // tutti i cookie ricevuti
    const cookies = response.headers['set-cookie']
    if (cookies) {
      // i cookie sono un array
      // si cerca il cookie di sessione PHP in questo array
      let trouvé = false
      let i = 0
      while (!trouvé && i < cookies.length) {
        // si cerca il cookie di sessione PHP
        const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
        if (results) {
          // si memorizza il cookie di sessione PHP
          const phpSessionCookie = results[1]
          // è presente al suo interno la stringa [deleted]?
          const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
          if (!results2) {
            // si ha il cookie di sessione corretto PHP
            this.phpSessionCookie = phpSessionCookie
            // abbiamo trovato
            trouvé = true
          } else {
            // elemento successivo
            i++
          }
        } else {
          // elemento successivo
          i++
        }
      }
    }
    // la risposta del server è in [response.data]
    return response.data
  }

In [getRemoteData] abbiamo mantenuto solo il codice che utilizza la risposta del server PHP per individuare il cookie di sessione PHP. Non è stato mantenuto il codice che includeva il cookie di sessione PHP nella richiesta al server PHP, poiché se ne occupa il browser che ospita il client [nuxt].

Una volta che il cookie di sessione PHP è stato ottenuto dal client [nuxt], esso deve essere inserito nella sessione [nuxt] affinché il server [nuxt] possa avvalersene. Non è il livello [dao] a occuparsene, ma esso fornisce l’accesso, tramite un metodo, al cookie di sessione PHP che ha memorizzato:


// accesso al cookie di sessione PHP
  getPhpSessionCookie() {
    return this.phpSessionCookie
}

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

  • occorre ricordare che il livello [dao] del client [nuxt] è persistente. Viene istanziato una volta e rimane poi in memoria;
  • finché il server PHP non invia un cookie di sessione PHP al client [nuxt], la funzione [getPhpSessionCookie] del client [nuxt] restituisce il valore [undefined];
  • quando il server PHP invia un cookie di sessione PHP al client [nuxt], quest’ultimo viene memorizzato in [this.phpSessionCookie] e vi rimarrà finché 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 per un aspetto: non invia direttamente il cookie di sessione PHP, poiché è il browser a farlo. Tuttavia, si è preferito mantenere due livelli [dao] distinti, poiché i ragionamenti che portano alle rispettive implementazioni sono diversi.

15.6. La sessione [nuxt]

Image

La sessione [nuxt] (tra client e server Nuxt) sarà incapsulata nel seguente oggetto [session]:


/* eslint-disable no-console */
// definizione della sessione
const session = {
  // contenuto della sessione
  value: {
    // store non inizializzato
    initStoreDone: false,
    // valore dello store Vuex
    store: ''
  },
  // salvataggio della sessione in un cookie
  save(context) {
    // salvataggio dello store nella sessione
    this.value.store = context.store.state
    console.log('nuxt-session save=', this.value)
    // salvataggio del valore della sessione
    context.app.$cookies.set('nuxt-session', this.value, { path: context.base, maxAge: context.env.maxAge })
  },
  // reset della sessione
  reset(context) {
    console.log('nuxt-session reset')
    // reset dello store
    context.store.commit('reset')
    // salvataggio del nuovo store nella sessione e salvataggio della sessione
    this.save(context)
  }
}
// esportazione della sessione
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] serve a salvare la sessione [nuxt] in un cookie. Qui si utilizza la libreria [cookie-universal-nuxt] per gestire il cookie. Si noti il nome del cookie della sessione [nuxt]: [nuxt-session] (riga 17);
  • righe 20-26: il metodo [reset] reinizializza la sessione [nuxt];
    • riga 23: lo store Vuex viene reinizializzato e poi salvato nella sessione, riga 25;

15.7. I plugin di gestione della sessione [nuxt]

Image

15.7.1. Il plugin di gestione della sessione [nuxt] di serveur [nuxt]

All'avvio dell'applicazione, il primo a entrare in funzione è il server [nuxt]. È quindi lui che inizializzerà la sessione [nuxt]. Lo script [server/plgSession] è il seguente:


/* eslint-disable no-console */

// importazione della sessione
import session from '@/entities/session'

export default (context, inject) => {
  // gestione della sessione sul server
  console.log('[plugin server plgSession]')

  // Esiste già una sessione?
  const value = context.app.$cookies.get('nuxt-session')
  if (!value) {
    // nuova sessione
    console.log("[plugin server plgSession], démarrage d'une nouvelle session")
  } else {
    // sessione esistente
    console.log("[plugin server plgSession], reprise d'une session existante")
    session.value = value
  }

  // si inietta una funzione in [context, Vue] che renderà la sessione corrente
  inject('session', () => session)
}
  • riga 4: si importa il codice della sessione [nuxt];
  • riga 11: si recupera il valore del cookie della sessione [nuxt];
  • righe 12-15: se il cookie della sessione [nuxt] non esistesse, allora la sessione [nuxt] importata alla riga 4 è sufficiente. Non c’è altro da fare;
  • righe 15-19: se il cookie della sessione [nuxt] esistesse, allora alla riga 18 se ne memorizza il valore nella sessione importata alla riga 4;
  • riga 22: la sessione è stata inizializzata o ripristinata. La si rende disponibile tramite la funzione [$session];

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

Lo script [client/plgSession] è il seguente:


/* eslint-disable no-console */

// importazione della sessione
import session from '@/entities/session'

export default (context, inject) => {
  // gestione della sessione del cliente
  console.log('[plugin client plgSession], reprise de la session [nuxt] du serveur')
  // si recupera la sessione esistente dal server Nuxt
  session.value = context.app.$cookies.get('nuxt-session')

  // si inserisce una funzione in [context, Vue] che renderà la sessione corrente
  inject('session', () => session)
}
  • riga 4: viene importata la sessione [nuxt];
  • riga 10: si recupera la sessione corrente [nuxt] dal cookie [nuxt-session];
  • riga 13: si restituisce la sessione [nuxt] importata alla riga 4 tramite la funzione iniettata [$session];

15.8. I plugin dei livelli [dao]

Image

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

Lo script [client/plgDao] è il seguente:


/* eslint-disable no-console */
// si crea un punto di accesso al livello [Dao]
import Dao from '@/api/client/Dao'
export default (context, inject) => {
  // configurazione di axios
  context.$axios.defaults.timeout = context.env.timeout
  context.$axios.defaults.baseURL = context.env.baseURL
  context.$axios.defaults.withCredentials = context.env.withCredentials
  // istanziazione del livello [dao]
  const dao = new Dao(context.$axios)
  // Inserimento di una funzione [$dao] nel contesto
  inject('dao', () => dao)
  // log
  console.log('[fonction client $dao créée]')
}
  • riga 3: viene importato il livello [dao] del client [nuxt];
  • righe 6-8: si configura l’oggetto [context.$axios] che effettuerà le richieste HTTP del livello [dao] del client [nuxt] con le informazioni del file [nuxt.config]:

// ambiente
  env: {
    // configurazione di Axios
    timeout: 2000,
    withCredentials: true,
    baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
    // configurazione del cookie di sessione [nuxt]
    maxAge: 60 * 5
  }
  • riga 10: viene istanziato il livello [dao] del client [nuxt];
  • riga 12: la funzione [$dao] viene inserita nel contesto e nelle pagine del cliente. Questa funzione consente di accedere al livello [dao] della riga 10;

Si noti quindi che per accedere al livello [dao] del client [nuxt] quando quest'ultimo viene eseguito, si scriverà:

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

15.8.2. Il plugin del livello [dao] di serveur [nuxt]

Lo script [server/plgDao] è il seguente:


/* eslint-disable no-console */
// si crea un punto di accesso al livello [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
  // configurazione di axios
  context.$axios.defaults.timeout = context.env.timeout
  context.$axios.defaults.baseURL = context.env.baseURL
  // si recupera il cookie di sessione
  const store = context.app.$session().value.store
  const phpSessionCookie = store ? store.phpSessionCookie : ''
  console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
  // istanziazione del livello [dao]
  const dao = new Dao(context.$axios, phpSessionCookie)
  // iniezione di una funzione [$dao] nel contesto
  inject('dao', () => dao)
  // log
  console.log('[fonction server $dao créée]')
}
  • riga 3: viene importato il livello [dao] dal server [nuxt];
  • righe 6-7: si configura l’oggetto [context.$axios] che effettuerà le richieste HTTP del livello [dao] del server [nuxt] utilizzando le informazioni contenute nel file [nuxt.config]:

// ambiente
  env: {
    // configurazione di Axios
    timeout: 2000,
    withCredentials: true,
    baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
    // configurazione del cookie di sessione [nuxt]
    maxAge: 60 * 5
  }
  • riga 9: si recupera lo store dell’applicazione [nuxt];
  • riga 10: se lo store esiste, si recupera il cookie della sessione PHP poiché è necessario per istanziare il livello [dao] del server [nuxt];
  • riga 13: si istanzia il livello [dao] del server [nuxt];
  • riga 15: la funzione [$dao] viene inserita nel contesto e nelle pagine del server [nuxt]. Questa funzione consente di accedere al livello [dao] della riga 13;

Si noti quindi che per accedere al livello [dao] del server [nuxt] quando questo è in esecuzione, si scriverà:

  • [context.app.$dao()] se 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 diversi componenti dell’applicazione [pages, client, serveur], senza che tali dati siano necessariamente reattivi.


/* eslint-disable no-console */

// stato dello store
export const state = () => ({
  // sessione jSON avviata
  jsonSessionStarted: false,
  // utente autenticato
  userAuthenticated: false,
  // cookie di sessione PHP
  phpSessionCookie: '',
  // adminData
  adminData: ''
})

// modifiche allo store
export const mutations = {
  // sostituzione dello stato
  replace(state, newState) {
    for (const attr in newState) {
      state[attr] = newState[attr]
    }
  },
  // reset dello store
  reset() {
    this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
  }
}

// azioni dello store
export const actions = {
  nuxtServerInit(store, context) {
    // chi esegue questo codice?
    console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
    // inizializza sessione
    initStore(store, context)
  }
}

function initStore(store, context) {
  // store è la sessione da inizializzare
  // si recupera la sessione
  const session = context.app.$session()
  // la sessione è già stata inizializzata?
  if (!session.value.initStoreDone) {
    // si avvia un nuovo store
    console.log("nuxtServerInit, initialisation d'une nouvelle session")
    // si inserisce lo store nella sessione
    session.value.store = store.state
    // lo store è ora inizializzato
    session.value.initStoreDone = true
  } else {
    console.log("nuxtServerInit, reprise d'un store existant")
    // si aggiorna lo store con quello della sessione
    store.commit('replace', session.value.store)
  }
  // si salva la sessione
  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 vero non appena l’inizializzazione di una sessione jSON con il server PHP sarà stata completata con successo, sia che essa sia stata effettuata dal client o dal server [nuxt]. Al termine di tale inizializzazione, il cookie di sessione con il server PHP sarà stato recuperato e inserito nella proprietà [phpSessionCookie], riga 10;
  • riga 8: [userAuthenticated] verrà impostato su vero non appena l’autenticazione presso il server PHP avrà avuto esito positivo, sia che sia stata effettuata dal client o dal server [nuxt];
  • riga 12: [adminData] sarà il valore [adminData] ottenuto dal server PHP una volta completata con successo l’autenticazione;
  • righe 18-22: l'assegnazione [replace] consente di inizializzare le proprietà precedenti con quelle di un oggetto passato come parametro;
  • righe 24-26: la mutazione [reset] ripristina i valori iniziali delle proprietà dello store;
  • 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 inserito nella sessione;
    • se lo store è già stato inizializzato, il suo valore viene recuperato dalla sessione [nuxt];
  • riga 42: si recupera la sessione Nuxt;
  • riga 44: si verifica se lo store è stato inizializzato:
    • se non lo è, si inserisce lo store iniziale nella sessione (riga 48);
    • poi, alla riga 50, si indica che lo store è stato inizializzato;
  • righe 51-55: se lo store era stato inizializzato, lo si utilizza, alla riga 54, per inizializzare lo store con il valore contenuto nella sessione;
  • riga 57: in ogni caso, la sessione viene salvata nel cookie [nuxt-session], insieme allo store in essa contenuto;

15.10. Il plugin [plgEventBus]

Image

Questo plugin ha lo scopo di rendere accessibile un bus di eventi al client [nuxt] tramite una funzione [$eventBus] iniettata nel contesto del client [nuxt]. È inutile iniettarlo nel contesto del server [nuxt] poiché quest’ultimo non è in grado di gestire gli eventi. Tuttavia, abbiamo già visto che iniettarlo sul lato server e poi utilizzarlo non provoca alcun errore.


/* eslint-disable no-console */
// si crea un bus di eventi tra le viste
import Vue from 'vue'
export default (context, inject) => {
  // il bus di eventi
  const eventBus = new Vue()
  // Inserimento di una funzione [$eventBus] nel contesto
  inject('eventBus', () => eventBus)
  // log
  console.log('[fonction $eventBus créée]')
}

Abbiamo già incontrato questo plugin nel paragrafo relativo ai link. La funzione [$eventBus] sarà disponibile sul client tramite le notazioni:

  • [context.app.$eventBus()] laddove 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:


<!-- disposizione delle viste -->
<template>
  <!-- riga -->
  <div>
    <b-row>
      <!-- area a tre colonne -->
      <b-col v-if="left" cols="3">
        <slot name="left" />
      </b-col>
      <!-- area a nove colonne -->
      <b-col v-if="right" cols="9">
        <slot name="right" />
      </b-col>
    </b-row>
  </div>
</template>

<script>
export default {
  // parametri
  props: {
    left: {
      type: Boolean
    },
    right: {
      type: Boolean
    }
  }
}
</script>

Il componente [navigation] è il seguente:


<template>
  <!-- menu Bootstrap a tre opzioni -->
  <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 "link":


<template>
  <div class="container">
    <b-card>
      <!-- un messaggio -->
      <b-alert show variant="success" align="center">
        <h4>[nuxt-12] : requêtes HTTP avec axios</h4>
      </b-alert>
      <!-- vista corrente del routing -->
      <nuxt />
      <!-- messaggio di attesa -->
      <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>
      <!-- errore in un'operazione asincrona -->
      <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
    }
  },
  // ciclo di vita
  beforeCreate() {
    console.log('[default beforeCreate]')
  },
  created() {
    console.log('[default created]')
    if (process.client) {
      // si sta ascoltando l'evento [loading]
      this.$eventBus().$on('loading', this.mShowLoading)
      // così come l'evento [errorLoadingMessage]
      this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
    }
  },
  beforeMount() {
    console.log('[default beforeMount]')
  },
  mounted() {
    console.log('[default mounted]')
  },
  methods: {
    // gestione del messaggio in attesa
    mShowLoading(value) {
      console.log('[default mShowLoading], showLoading=', value)
      this.showLoading = value
    },
    // errore in un'operazione asincrona
    mShowErrorLoading(value, errorLoadingMessage) {
      console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
      this.showErrorLoading = value
      this.errorLoadingMessage = errorLoadingMessage
    }
  }
}
</script>
  • righe 10-14: visualizzano il messaggio di attesa della fine di un'operazione asincrona del client [nuxt];
  • righe 15-18: visualizzano l’eventuale messaggio di errore di 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] si mette in ascolto degli eventi:
    • [loading] che segnala l’inizio o la fine di un’attesa. Viene quindi eseguita la funzione [mShowLoading];
    • [errorLoading], che segnala la necessità di visualizzare un messaggio di errore. Viene quindi eseguita la funzione [mShowErrorLoading];
  • le pagine [nuxt]:
    • visualizzano il messaggio di attesa emettendo l’evento [‘loading’, true] sul bus degli eventi;
    • nascondono il messaggio di attesa 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 inviando 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):


<!-- definizione della vista HTML -->
<template>
  <!-- impaginazione -->
  <Layout :left="true" :right="true">
    <!-- avviso nella colonna di destra -->
    <template slot="right">
      <!-- messaggio su sfondo rosa -->
      <b-alert show variant="danger" align="center">
        <h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
      </b-alert>
    </template>
    <!-- menu di navigazione nella colonna di sinistra -->
    <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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  // proprietà [props]
  props: { error: { type: Object, default: () => 'waiting ...' } },
  // ciclo di vita
  beforeCreate() {
    // client e server
    console.log('[error beforeCreate]')
  },
  created() {
    // client e server
    console.log('[error created, error=]', this.error)
  },
  beforeMount() {
    // solo client
    console.log('[error beforeMount]')
  },
  mounted() {
    // solo client
    console.log('[error mounted]')
  }
}
</script>

15.13. La pagina [index] eseguita dal server [nuxt]

Image

La pagina [index.vue] ha la particolarità di essere accessibile solo tramite il server [nuxt]. All’utente non viene presentato alcun link per accedervi tramite il client [nuxt]. Il suo codice è il seguente:


<!-- pagina principale -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio-->
    <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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  // dati asincroni
  async asyncData(context) {
    // log
    console.log('[index asyncData started]')
    try {
      // si avvia una sessione jSON
      const dao = context.app.$dao()
      const response = await dao.initSession()
      // log
      console.log('[index asyncData response=]', response)
      // si recupera il cookie di sessione PHP per le richieste successive
      const phpSessionCookie = dao.getPhpSessionCookie()
      // si memorizza il cookie di sessione PHP nella sessione [nuxt]
      context.store.commit('replace', { phpSessionCookie })
      // Si è verificato un errore?
      if (response.état !== 700) {
        // l'errore si trova in response.réponse
        throw new Error(response.réponse)
      }
      // si nota che la sessione jSON è stata avviata
      context.store.commit('replace', { jsonSessionStarted: true })
      // si restituisce il risultato
      return { result: '[succès]' }
    } catch (e) {
      // log
      console.log('[index asyncData error=]', e)
      // si rileva che la sessione jSON non è stata avviata
      context.store.commit('replace', { jsonSessionStarted: false })
      // si segnala l'errore
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // si salva lo store
      const session = context.app.$session()
      session.save(context)
      // log
      console.log('[index asyncData finished]')
    }
  },
  // ciclo di vita
  beforeCreate() {
    console.log('[index beforeCreate]')
  },
  created() {
    console.log('[index created]')
  },
  beforeMount() {
    console.log('[index beforeMount]')
  },
  mounted() {
    console.log('[index mounted]')
    // solo client
    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’apertura di una sessione jSON con il server di calcolo delle imposte;
  • riga 25: si sa che quando la pagina viene richiesta direttamente al server [nuxt], la funzione [asyncData] viene eseguita solo dal server e non dal client [nuxt], che viene eseguito quando il browser ha ricevuto la risposta dal server [nuxt];
  • riga 30: si recupera il livello [dao] nel contesto del server [nuxt];
  • riga 35: se il server non aveva ancora effettuato una richiesta al server di calcolo delle imposte, riceve il suo primo cookie di sessione PHP, altrimenti l’ultimo cookie di sessione PHP che ha ricevuto (vedere il codice del livello [dao] del server [nuxt] al paragrafo «link»);
  • riga 37: si memorizza questo cookie di sessione PHP nello store;
  • righe 39-42: si verifica se l’operazione è andata a buon fine. In caso contrario, viene generata un’eccezione che verrà intercettata dal [catch] della riga 47;
  • riga 44: si registra nello store che la sessione jSON con il server PHP è stata avviata;
  • riga 46: viene restituito il risultato [result], che viene visualizzato alla riga 7;
  • righe 47-54: viene gestita un’eventuale eccezione. Questa può essere di due tipi:
    • l’operazione HTTP della riga 31 è fallita a causa di un errore di comunicazione tra il server [nuxt] e il server PHP;
    • l’operazione HTTP della riga 31 è andata a buon fine, ma il risultato ricevuto ha segnalato un errore (righe 39-42);
  • riga 51: si nota che la sessione jSON con il server PHP non è stata avviata;
  • riga 53: viene restituito il risultato [result], visualizzato alla riga 7. Inoltre, si impostano le proprietà [showErrorLoading] e [errorLoadingMessage] che il client [nuxt] utilizzerà per visualizzare un messaggio di errore quando riceverà la pagina inviata dal server [nuxt] (righe 72-79);
  • righe 54-60: codice eseguito in ogni caso (successo o fallimento);
  • riga 56: si recupera la sessione [nuxt] nel contesto del server [nuxt];
  • riga 57: la si salva;
  • 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] può fallire, ad esempio, se il server di calcolo delle imposte non è in esecuzione al momento dell’avvio 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] gliel’ha inviata. Quest’ultimo gli ha inviato le informazioni [result] ed eventualmente [showErrorLoading] e [errorLoadingMessage].

È noto che la funzione [asyncData] non verrà eseguita. Restano quindi le funzioni del ciclo di vita e in particolare la funzione [mounted]:


mounted() {
    console.log('[index mounted]')
    // solo client
    if (this.showErrorLoading) {
      console.log('[index mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
}
  • il client [nuxt] integra automaticamente nelle proprietà della pagina gli elementi [result] ed eventualmente [showErrorLoading, errorLoadingMessage] che gli sono stati inviati dal server [nuxt]:
  • la proprietà [result] viene visualizzata alla riga 7;
  • le proprietà [showErrorLoading, errorLoadingMessage] vengono utilizzate dal metodo [mounted]: alla riga 4 viene verificata la proprietà [showErrorLoading]. Se è vera, alla riga 6 si utilizza il bus degli eventi del client [nuxt] per segnalare che c’è un messaggio di errore da visualizzare;
  • l’evento [errorLoading] generato alla riga 6 viene intercettato dalla pagina [layouts/default] descritta nel paragrafo «link»;

15.15. La pagina [authentification] viene eseguita dal server [nuxt]

La pagina [authentification] ha il compito di identificare un utente presso il server di calcolo delle imposte. Il suo codice è il seguente:


<!-- pagina di autenticazione -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio-->
    <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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  // dati asincroni
  async asyncData(context) {
    // log
    console.log('[authentification asyncData started]')
    if (process.client) {
      // inizio attesa del client [nuxt]
      context.app.$eventBus().$emit('loading', true)
      // nessun errore
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // autenticazione sul server
      const dao = context.app.$dao()
      const response = await dao.authentifierUtilisateur('admin', 'admin')
      // log
      console.log('[authentification asyncData response=]', response)
      // risultato
      const userAuthenticated = response.état === 200
      // si registra se l'utente è autenticato o meno
      context.store.commit('replace', { userAuthenticated })
      // si salva lo store nella sessione [nuxt]
      const session = context.app.$session()
      session.save(context)
      // errore di autenticazione?
      if (!userAuthenticated) {
        // l'errore si trova in response.réponse
        throw new Error(response.réponse)
      }
      // si ottiene il risultato
      return { result: '[succès]' }
    } catch (e) {
      // si segnala l'errore
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // log
      console.log('[authentification asyncData finished]')
      if (process.client) {
        // fine attesa del client [nuxt]
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  // ciclo di vita
  beforeCreate() {
    console.log('[authentification beforeCreate]')
  },
  created() {
    console.log('[authentification created]')
  },
  beforeMount() {
    console.log('[authentification beforeMount]')
  },
  mounted() {
    console.log('[authentification mounted]')
    // solo cliente
    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: si recupera il livello [dao] dal server [nuxt];
  • riga 37: si effettua l'autenticazione presso il server di calcolo delle imposte con 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 la risposta è di stato 200;
  • riga 43: si inserisce nel store la proprietà [userAuthenticated];
  • righe 44-46: lo store viene salvato nella sessione [nuxt];
  • righe 48-51: se l’autenticazione non è andata a buon fine, viene generata un’eccezione con il messaggio di errore inviato dal server di calcolo delle imposte;
  • altrimenti, alla riga 53, si restituisce un risultato di successo che verrà visualizzato alla riga 7;
  • righe 54-57: in caso di errore si impostano 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 risultato, questo viene visualizzato alla riga 7. Successivamente vengono eseguiti i metodi [beforeCreate] (righe 67-69) e [created] (righe 70-72);
  • il processo è terminato;

Nota: l’esecuzione della pagina [authentification] da parte del server [nuxt] potrebbe non andare a buon fine, ad esempio se la sessione jSON con il server di calcolo delle imposte non è stata inizializzata. È possibile farlo nel modo seguente:

  • eliminare il cookie di sessione PHP dal proprio browser (per ricominciare da zero):

Image

  • avviare l’applicazione [nuxt] mentre il server di calcolo non è stato avviato: si otterrà un errore;
  • avviare il server di calcolo delle imposte;
  • digitare URL [/authentification] direttamente nella barra degli indirizzi del browser:

Image

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

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

Riprendiamo il codice della pagina:


<!-- pagina di autenticazione -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio-->
    <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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  // dati asincroni
  async asyncData(context) {
    // log
    console.log('[authentification asyncData started]')
    if (process.client) {
      // inizio attesa del client [nuxt]
      context.app.$eventBus().$emit('loading', true)
      // nessun errore
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // autenticazione sul server
      const dao = context.app.$dao()
      const response = await dao.authentifierUtilisateur('admin', 'admin')
      // log
      console.log('[authentification asyncData response=]', response)
      // risultato
      const userAuthenticated = response.état === 200
      // si registra se l'utente è autenticato o meno
      context.store.commit('replace', { userAuthenticated })
      // si salva lo store nella sessione [nuxt]
      const session = context.app.$session()
      session.save(context)
      // errore di autenticazione?
      if (!userAuthenticated) {
        // l'errore si trova in response.réponse
        throw new Error(response.réponse)
      }
      // viene restituito il risultato
      return { result: '[succès]' }
    } catch (e) {
      // si segnala l'errore
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // log
      console.log('[authentification asyncData finished]')
      if (process.client) {
        // fine attesa del client [nuxt]
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  // ciclo di vita
  beforeCreate() {
    console.log('[authentification beforeCreate]')
  },
  created() {
    console.log('[authentification created]')
  },
  beforeMount() {
    console.log('[authentification beforeMount]')
  },
  mounted() {
    console.log('[authentification mounted]')
    // solo cliente
    if (this.showErrorLoading) {
      console.log('[authentification mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>

Esistono due casi di esecuzione della pagina [authentification] da parte del client [nuxt]:

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

Image

Esaminiamo innanzitutto il primo caso. In questo caso, il client [nuxt] non esegue la funzione [asyncData]. Inserisce nelle proprietà della pagina gli elementi [result] ed eventualmente [showErrorLoading, errorLoadingMessage] che gli sono stati inviati dal server [nuxt]:

  • 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 si utilizza il bus degli eventi del client [nuxt] per segnalare che c’è un messaggio di errore da visualizzare;

Il meccanismo di visualizzazione del messaggio di errore è stato spiegato per la pagina [index] nel paragrafo relativo al link.

Il caso 2 riguarda il client [nuxt], eseguito quando l’utente fa clic sul link [Authentification]. In questo caso, il client [nuxt] viene eseguito in modo autonomo 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 la visualizzazione del messaggio di attesa e la rimozione di un eventuale messaggio di errore che sarebbe stato visualizzato in precedenza;
  • riga 36: ora viene recuperato il livello [dao] del client [nuxt];
  • righe 60-63: il client [nuxt] richiede la fine della visualizzazione del messaggio di attesa;
  • una volta terminata [asyncData], avrà inizio il ciclo di vita della pagina. Verrà eseguita la funzione [mounted] delle righe 76-83. Se si è verificato un errore, verrà visualizzato il messaggio di errore;

Nota: per provocare un errore, seguire la procedura illustrata per il server [nuxt] alla fine del paragrafo «link», ma invece di richiedere la pagina [authentification] digitando il suo URL URL nella barra degli indirizzi, utilizzate il link [Authentification] dal menu di navigazione. In questo modo verrà eseguito il client [nuxt].

15.17. La pagina [get-admindata]

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


<!-- vista get-admindata -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio -->
    <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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  // dati asincroni
  async asyncData(context) {
    // log
    console.log('[get-admindata asyncData started]')
    if (process.client) {
      // inizio attesa
      context.app.$eventBus().$emit('loading', true)
      // nessun errore
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // richiesta del dato [admindata]
      const response = await context.app.$dao().getAdminData()
      // log
      console.log('[get-admindata asyncData response=]', response)
      // risultato
      const adminData = response.état === 1000 ? response.réponse : ''
      // si inseriscono i dati nello store
      context.store.commit('replace', { adminData })
      // si salva lo store nella sessione [nuxt]
      const session = context.app.$session()
      session.save(context)
      // si è verificato un errore?
      if (!adminData) {
        // l'errore si trova in response.réponse
        throw new Error(response.réponse)
      }
      // si restituisce il valore ricevuto
      return { result: adminData }
    } catch (e) {
      // si segnala l'errore
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // log
      console.log('[get-admindata asyncData finished]')
      if (process.client) {
        // fine attesa
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  // ciclo di vita
  beforeCreate() {
    console.log('[get-admindata beforeCreate]')
  },
  created() {
    console.log('[get-admindata created]')
  },
  beforeMount() {
    console.log('[get-admindata beforeMount]')
  },
  mounted() {
    console.log('[get-admindata mounted]')
    // cliente
    if (this.showErrorLoading) {
      console.log('[get-admindata mounted, showErrorLoading=true]')
      this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
    }
  }
}
</script>

Questa pagina è molto simile alla pagina [authentification]. Le spiegazioni sono analoghe sia per la sua esecuzione da parte del server [nuxt] sia per la sua esecuzione da parte del client [nuxt]. Si noti tuttavia che la riga 7 non visualizza «successo» o «fallimento» come in precedenza, ma il valore dei dati ricevuti dal server di calcolo delle imposte (riga 52):

Image

Il risultato sopra riportato si ottiene sia con il server che con il client [nuxt]. Per generare un errore, richiedete 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:


<!-- pagina principale -->
<template>
  <Layout :left="true" :right="true">
    <!-- navigazione -->
    <Navigation slot="left" />
    <!-- messaggio-->
    <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',
  // componenti utilizzati
  components: {
    Layout,
    Navigation
  },
  // dati asincroni
  async asyncData(context) {
    // log
    console.log('[fin-session asyncData started]')
    // caso del cliente [nuxt]
    if (process.client) {
      // inizio attesa
      context.app.$eventBus().$emit('loading', true)
      // nessun errore
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // viene richiesta una nuova sessione PHP al server di calcolo delle imposte
      const dao = context.app.$dao()
      const response = await dao.finSession()
      // log
      console.log('[fin-session asyncData response=]', response)
      // si è verificato un errore?
      if (response.état !== 400) {
        // l'errore si trova in response.réponse
        throw new Error(response.réponse)
      }
      // il server ha inviato un nuovo cookie di sessione PHP
      // viene recuperato sia dal server che dal client Nuxt
      // se questo codice viene eseguito dal client [nuxt], il cookie di sessione PHP deve essere inserito nella sessione Nuxt
      // affinché il plugin [plgDao] del server [nuxt] possa recuperarlo e inizializzare il livello [dao] con
      //; se questo codice viene eseguito dal server [nuxt], il cookie di sessione PHP deve essere inserito nella sessione Nuxt
      // affinché il routing del client [nuxt] lo recuperi e lo trasmetta al browser
      const phpSessionCookie = dao.getPhpSessionCookie()
      // si registra nello store l’avvio della sessione jSON e si memorizza il cookie di sessione PHP
      context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
      // si salva lo store nella sessione [nuxt]
      const session = context.app.$session()
      session.save(context)
      // si restituisce il risultato
      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)
      // si segnala l'errore
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // log
      console.log('[fin-session asyncData finished]')
      if (process.client) {
        // fine attesa
        context.app.$eventBus().$emit('loading', false)
      }
    }
  },
  // ciclo di vita
  beforeCreate() {
    console.log('[fin-session beforeCreate]')
  },
  created() {
    console.log('[fin-session created]')
  },
  beforeMount() {
    console.log('[fin-session beforeMount]')
  },
  mounted() {
    console.log('[fin-session mounted]')
    // solo cliente
    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. È necessario soffermarsi solo su un punto: l’operazione asincrona della riga 38 fa sì che il server di calcolo dell’imposta invii un nuovo cookie di sessione PHP. Le spiegazioni relative alla gestione di questo cookie variano a seconda che sia il server o il client [nuxt] a eseguire questo codice.

Cominciamo dal server [nuxt]:

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

// costruttore
  constructor(axios, phpSessionCookie) {
    // libreria axios
    this.axios = axios
    // valore del cookie di sessione
    this.phpSessionCookie = phpSessionCookie
    // nome del cookie di sessione del server PHP
    this.phpSessionCookieName = 'PHPSESSID'
}

Si nota alla riga 1 che il costruttore necessita del cookie di sessione PHP del momento, l’ultimo ricevuto, sia dal server che dal client [nuxt];

  • riga 52: il server [nuxt] recupera il cookie della nuova sessione PHP oppure il cookie precedente se l’operazione di chiusura 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 [fin-session] con i dati inviati dal server. Sappiamo che non eseguirà la funzione [asyncData];
  • alla fine, dopo che il server e il client [nuxt] hanno completato il loro lavoro, sappiamo che il cookie PHP necessario per gli scambi 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, poiché è da lì che il suo livello [dao] lo preleverà. Nel plugin [server/plgDao] che inizializza il livello [dao] del server, è stato scritto:


/* eslint-disable no-console */
// si crea un punto di accesso al livello [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
  // configurazione di Axios
  context.$axios.defaults.timeout = context.env.timeout
  context.$axios.defaults.baseURL = context.env.baseURL
  // si recupera il cookie di sessione
  const store = context.app.$session().value.store
  const phpSessionCookie = store ? store.phpSessionCookie : ''
  console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
  // istanziazione del livello [dao]
  const dao = new Dao(context.$axios, phpSessionCookie)
  // iniezione di una funzione [$dao] nel contesto
  inject('dao', () => dao)
  // log
  console.log('[fonction server $dao créée]')
}
  • alla 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. Infatti non è lui a inviare il cookie, ma il browser che lo esegue. Tuttavia, questo browser non conosce il cookie della nuova sessione PHP ricevuto dal server [nuxt]. Se si utilizzano i link del menu di navigazione [3]:

Image

Il server di calcolo delle imposte riceverà dal browser un cookie di sessione PHP obsoleto e risponderà che a tale cookie non è associata alcuna sessione jSON. Dobbiamo trovare il modo di trasmettere al browser il nuovo cookie di sessione PHP.

A tal fine è possibile utilizzare un middleware di routing:

Image

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


// router
  router: {
    // radice dei URL dell'applicazione
    base: '/nuxt-12/',
    // middleware di routing
    middleware: ['routing']
},

Lo script [middleware/routing] è il seguente:


/* eslint-disable no-console */

// si importa il middleware del client
import clientRouting from './client/routing'

export default function(context) {
  // chi esegue questo codice?
  console.log('[middleware], process.server', process.server, ', process.client=', process.client)
  if (process.client) {
    // routing client
    clientRouting(context)
  }
}
  • righe 9-12: si esegue il routing solo del client con una funzione importata alla riga 4;

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


/* eslint-disable no-console */
export default function(context) {
  // chi esegue questo codice?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // gestione del cookie di sessione PHP nel browser
  // il cookie di sessione PHP del browser deve essere identico a quello presente nella sessione Nuxt
  // l'azione [fin-session] riceve un nuovo cookie PHP (sia dal server che dal client Nuxt)
  // se è il server a riceverlo, il client deve trasmetterlo al browser
  // per le proprie comunicazioni con il server PHP
  //: in questo caso si tratta di un routing client

  // si recupera il cookie di sessione PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // se esiste, si assegna il cookie di sessione PHP al browser
    document.cookie = phpSessionCookie
  }
}

Torniamo alla situazione subito dopo l’esecuzione della pagina [fin-session] da parte del server [nuxt]:

Image

Se si clicca su uno dei link del menu [3], il client [nuxt] prenderà il controllo. Poiché si verificherà un cambio di pagina, verrà eseguito lo script di routing del client:

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

Lo script [client/routing] viene eseguito ad ogni cambio di pagina del client [nuxt]. Il codice dello script è valido indipendentemente dalla pagina di destinazione: semplicemente, nella maggior parte dei casi, assegna al browser un cookie di sessione PHP che esso possiede già, 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 [fin-session] come appena spiegato;

Ora esaminiamo il caso in cui la pagina [fin-session] venga eseguita esclusivamente dal client [nuxt], poiché si è cliccato sul relativo link nel menu di navigazione. Ora è il client [nuxt] che esegue la funzione [asyncData]:


try {
      // si richiede una nuova sessione PHP al server di calcolo delle imposte
      const dao = context.app.$dao()
      const response = await dao.finSession()
      // log
      console.log('[fin-session asyncData response=]', response)
      // si è verificato un errore?
      if (response.état !== 400) {
        // l'errore si trova in response.réponse
        throw new Error(response.réponse)
      }
      // il server ha inviato un nuovo cookie di sessione PHP
      // viene recuperato sia dal server che dal client Nuxt
      // se questo codice viene eseguito dal client [nuxt], il cookie di sessione PHP deve essere inserito nella sessione Nuxt
      // affinché il plugin [plgDao] del server [nuxt] possa recuperarlo e inizializzare il livello [dao] con
      //; se questo codice viene eseguito dal server [nuxt], il cookie di sessione PHP deve essere inserito nella sessione Nuxt
      // affinché il routing del client [nuxt] lo recuperi e lo trasmetta al browser
      const phpSessionCookie = dao.getPhpSessionCookie()
      // si registra nello store l’avvio della sessione jSON e si memorizza il cookie di sessione PHP
      context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
      // si salva lo store nella sessione [nuxt]
      const session = context.app.$session()
      session.save(context)
      // viene restituito il risultato
      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)
      // si segnala l'errore
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // log
      console.log('[fin-session asyncData finished]')
      if (process.client) {
        // fine attesa
        context.app.$eventBus().$emit('loading', false)
      }
    }
  • riga 3: qui viene recuperato il livello [dao] del client [nuxt];
  • riga 18: il cookie di sessione PHP recuperato dal livello [dao] del client [nuxt] viene memorizzato, inserito nello store (riga 20) e successivamente salvato nella sessione [nuxt] (righe 22-23);
  • da questo punto in poi tutto procede correttamente, poiché sappiamo che il livello [dao] del server [nuxt] recupererà il cookie di sessione PHP dalla sessione [nuxt];

15.19. Exécution

Per eseguire questo esempio, è necessario assicurarsi, prima dell’esecuzione, di eliminare il cookie di sessione [nuxt] e il cookie PHP dal browser che esegue il client [nuxt], in modo da partire da una situazione pulita. Di seguito un esempio con il browser Chrome:

Image

15.20. Conclusion

Questo esempio si è rivelato particolarmente complesso. Ha richiesto l’applicazione delle conoscenze acquisite negli esempi precedenti: persistenza dello store in una sessione [nuxt], plugin per l’iniezione di funzioni, middleware di routing, gestione degli errori nelle 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 che ciò causasse il malfunzionamento dell’applicazione. Per questo motivo, siamo stati costretti ad analizzare il comportamento di ciascuna pagina a seconda che venisse eseguita dal client o dal server [nuxt].

Questa uniformità di comportamento tra client e server [nuxt] non è indispensabile. Si può infatti considerare il caso frequente in cui:

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

Tuttavia, anche in questo caso, è necessario verificare il risultato dell’esecuzione di tutte le pagine da parte del server [nuxt], poiché è proprio questo il risultato che otterranno i motori di ricerca che le richiederanno.