Skip to content

16. Esempio [nuxt-13]: controllo della navigazione di [nuxt-12]

In questo esempio, ci concentriamo sulla navigazione di [nuxt-12]. Non l’abbiamo fatto in [nuxt-12] perché il controllo della navigazione avrebbe reso ancora più complesso un esempio già di per sé complesso.

Obiettivo: vogliamo che l’utente possa eseguire solo azioni autorizzate:

  • se la sessione jSON non è stata avviata, allora è consentita solo la sessione URL [/];
  • se la sessione jSON è stata avviata ma l’utente non è autenticato, allora sono consentite solo le sessioni URL e [/authentification];
  • se la sessione jSON è stata avviata e l’utente è autenticato, allora sono autorizzate solo le sessioni URL e [/get-admindata, /fin-session];
  • quando la destinazione dell'instradamento corrente non è autorizzata, si procederà a un reindirizzamento verso una URL autorizzata;

L’esempio [nuxt-13] si ottiene inizialmente copiando l’esempio [nuxt-12]:

Image

È nella cartella di instradamento [middleware] che verranno apportate le modifiche.

16.1. Routing dell’applicazione [nuxt]

Il routing dell’applicazione è configurato come segue nel file [nuxt.config]:


// router
  router: {
    // radice dei URL dell'applicazione
    base: '/nuxt-13/',
    // middleware di routing
    middleware: ['routing']
},
  • riga 6: l’instradamento dell’applicazione è controllato dal file [middleware/routing];

Il file [middleware/routing] è il seguente:


/* eslint-disable no-console */

// si importano i middleware del server e del client
import serverRouting from './server/routing'
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.server) {
    // routing server
    serverRouting(context)
  } else {
    // instradamento client
    clientRouting(context)
  }
}
  • righe 10-16: l'instradamento del client e del server [nuxt] viene gestito in modo diverso. Questo è un punto in cui differiscono notevolmente;
  • riga 4: il routing del server è implementato dallo script [middleware/server/routing];
  • riga 5: il routing del client è implementato dallo script [middleware/client/routing];

16.2. Routing del client [nuxt]

L'instradamento del client [nuxt] rimane invariato rispetto a [nuxt-12]:


/* 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 presente, si assegna il cookie di sessione PHP al browser
    document.cookie = phpSessionCookie
  }

  ...
}

Per evitare che il cliente acceda a percorsi non autorizzati, gli verranno semplicemente proposti nel menu di navigazione del cliente solo i percorsi autorizzati. Il componente [components/navigation] diventa il seguente:


<template>
  <!-- menu Bootstrap a tre opzioni -->
  <b-nav vertical>
    <b-nav-item v-if="$store.state.jsonSessionStarted && !$store.state.userAuthenticated" to="/authentification" exact exact-active-class="active">
      Authentification
    </b-nav-item>
    <b-nav-item
      v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated && !$store.state.adminData"
      to="/get-admindata"
      exact
      exact-active-class="active"
    >
      Requête AdminData
    </b-nav-item>
    <b-nav-item v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated" to="/fin-session" exact exact-active-class="active">
      Fin session impôt
    </b-nav-item>
  </b-nav>
</template>
  • riga 4: l’opzione [Authentification] viene proposta solo se la sessione jSON è stata avviata ma l’utente non è autenticato. Se la sessione jSON non è stata avviata o se l’utente è già autenticato, l’opzione non viene proposta;
  • righe 7-11: l’opzione [Requête AdminData] è disponibile solo se la sessione jSON è stata avviata, l’utente è autenticato e il dato [AdminData] non è stato ancora recuperato. Se una di queste tre condizioni non è soddisfatta (sessione jSON non avviata, utente non autenticato o dato [AdminData] già recuperato), l’opzione non è disponibile;
  • riga 15: l’opzione [Fin session impôt] è disponibile non appena la sessione jSON è stata avviata e l’utente è autenticato, altrimenti non lo è;

16.3. Routing del server [nuxt]

L’instradamento del server è in genere più complesso di quello del client, poiché l’utente può digitare qualsiasi URL nella barra degli indirizzi del proprio browser. Si può lasciare che ciò avvenga (dopotutto l’utente non dovrebbe farlo) oppure cercare di controllare la situazione. È ciò che faremo qui, a titolo di esempio, poiché nel caso dell’applicazione [nuxt-12], si può tranquillamente farne a meno, dato che il server di calcolo delle imposte è ben protetto contro questi URL inseriti manualmente e sa inviare i messaggi di errore adeguati. Lo abbiamo visto in [next-12], dove non c’era alcun controllo di instradamento.

L’instradamento di un server [nuxt] è molto diverso da quello di un client [nuxt] per quanto riguarda il concetto di reindirizzamento:

  • quando un server [nuxt] viene reindirizzato, invia un comando di reindirizzamento al browser client indicando la destinazione del reindirizzamento. Il browser effettua quindi una nuova richiesta al server [nuxt], chiedendogli la destinazione che gli è stata trasmessa. È come se l’utente avesse digitato manualmente l’indirizzo URL della destinazione del reindirizzamento: l’intera applicazione [nuxt] si riavvia e quindi tutto il suo ciclo di vita (plugin del server, store, routing del server, pagine);
  • quando un client [nuxt] viene reindirizzato, non accade nulla di simile. Si verifica un semplice cambio di pagina, lo stesso che si sarebbe ottenuto se l’utente avesse cliccato su un link che porta alla destinazione del reindirizzamento. Il ciclo di vita è quindi diverso (routing client, visualizzazione della destinazione del percorso);

Per questo motivo, è preferibile separare l’instradamento client da quello server, anche se i due codici possono sembrare simili.

Lo script di routing del server [middleware/server/routing] sarà il seguente:


/* eslint-disable no-console */
export default function(context) {
  // chi esegue questo codice?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)

  // si recuperano alcune informazioni dallo store [nuxt]
  const store = context.store
  // da dove veniamo?
  const from = store.state.from || 'nowhere'
  ...
}
  • nel routing client, la funzione di routing riceve il contesto [context] con la proprietà [context.from], che rappresenta il percorso della pagina da cui si proviene. Il percorso verso cui ci si sta dirigendo è ottenuto tramite [context.route];
  • nel routing del server, la funzione di routing riceve il contesto [context] senza la proprietà [context.from]. L’instradamento del server interviene solo quando un URL viene richiesto manualmente al server [nuxt]. Si sa che in tal caso l’intera applicazione [nuxt] viene reinizializzata. È come se si ripartisse da zero e quindi non esiste il concetto di «pagina precedente»;
  • grazie alla sessione [nuxt] sappiamo che il server può recuperare questa sessione e quindi non ripartire da zero. È quindi in questa sessione [nuxt] e, più in particolare, nello store di questa sessione che memorizzeremo il nome dell’ultima pagina visualizzata dal browser client prima che venga richiesta una URL al server [nuxt];
  • righe 7-9: si recupera il nome dell’ultima pagina visualizzata dal browser client. All’avvio dell’applicazione, questa informazione [from] non esiste nello store. Si assegna quindi il nome [nowhere] alla variabile [from];

Affinché il server [nuxt] possa recuperare dallo store il nome dell’ultima pagina visualizzata dal browser client, è necessario che anche il client [nuxt] inserisca questa informazione nello store. Lo script di routing del client [nuxt] viene quindi completato come segue:


/* 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 presente, si assegna il cookie di sessione PHP al browser
    document.cookie = phpSessionCookie
  }

  // si inserisce nella sessione il nome della pagina a cui si sta andando - nessun reindirizzamento del server
  context.store.commit('replace', { serverRedirection: false, from: context.route.name })
  // si salva lo store nella sessione [nuxt]
  const session = context.app.$session()
  session.value.store = context.store.state
  session.save(context)
}
  • vengono aggiunte le righe 19-24;
  • riga 20: si inserisce nello store il nome della pagina [context.route.name] che verrà visualizzata e che sarà quindi, durante il successivo routing, la pagina da cui si proviene. Inoltre, vedremo che nell’instradamento del server [nuxt], quest’ultimo deve sapere se l’instradamento in corso deriva da un precedente reindirizzamento del server [nuxt]. In questo caso non è così, e quindi si imposta la proprietà [serverRedirection] su [false];
  • righe 22-24: lo stato dello store viene inserito nella sessione [nuxt] (riga 23), quindi la sessione [nuxt] viene salvata in un cookie (riga 24) che a sua volta verrà salvato nel browser del cliente [nuxt];

Torniamo allo script di routing del server [nuxt]:


/* eslint-disable no-console */
export default function(context) {
  // chi esegue questo codice?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)

  // si recuperano alcune informazioni dallo store [nuxt]
  const store = context.store
  // da dove veniamo?
  const from = store.state.from || 'nowhere'
  // Dove stiamo andando?
  const to = context.route.name
  // eventuale reindirizzamento
  let redirection = ''
  // gestione del routing completata
  let done = false

  // si è già in un reindirizzamento del server [nuxt]?
  if (store.state.serverRedirection) {
    // nulla da fare
    done = true
  }

  // Si tratta di un ricaricamento della pagina?
  if (to === from) {
    // niente da fare
    done = true
  }
  
  // controllo della navigazione del server [nuxt]
  // ci si basa sulla navigazione del client nel componente [navigation]

  // caso in cui la sessione PHP non sia stata avviata
  if (!done && !store.state.jsonSessionStarted && to !== 'index') {
    // reindirizzamento
    redirection = 'index'
    // operazione completata
    done = true
  }

  // nel caso in cui l'utente non sia autenticato
  if (!done && store.state.jsonSessionStarted && !store.state.userAuthenticated && to !== 'authentification') {
    // reindirizzamento
    redirection = from
    // lavoro completato
    done = true
  }

  // caso in cui l'utente è stato autenticato
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && to !== 'get-admindata' && to !== 'fin-session') {
    // si rimane sulla stessa pagina
    redirection = from
    // operazione completata
    done = true
  }

  // caso in cui è stato ottenuto [adminData]
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && store.state.adminData && to !== 'fin-session') {
    // si rimane sulla stessa pagina
    redirection = from
    // lavoro completato
    done = true
  }

  // sono stati effettuati tutti i controlli ---------------------
  // reindirizzamento?
  if (redirection) {
    // si registra il reindirizzamento nello store
    store.commit('replace', { serverRedirection: true })
  } else {
    // nessun reindirizzamento
    store.commit('replace', { serverRedirection: false, from: to })
  }
  // si salva lo store nella sessione [nuxt]
  const session = context.app.$session()
  session.value.store = store.state
  session.save(context)
  // si esegue l'eventuale reindirizzamento
  if (redirection) {
    context.redirect({ name: redirection })
  }
}
  • righe 6-9: si recupera il valore di [from] nella memoria del server [nuxt];
  • riga 11: si registra la destinazione dell’instradamento corrente;
  • riga 13: l'instradamento può comportare un reindirizzamento del browser client. [redirection] sarà la destinazione di tale reindirizzamento;
  • riga 15: il passaggio da [done] a [true] indica che l'instradamento è terminato;
  • righe 17-21: si verifica innanzitutto se l’instradamento corrente deriva da una richiesta di reindirizzamento inviata al browser del cliente. Questa informazione è memorizzata nella proprietà [serverRedirection] dello store. Se questa proprietà è vera, significa che il server [nuxt] ha inviato un reindirizzamento al browser client durante la precedente richiesta al server [nuxt]. In questo caso, non è necessario effettuare alcun instradamento. Durante la richiesta precedente, il router del server [nuxt] ha deciso che il browser client doveva essere reindirizzato. Questa decisione non deve essere rimessa in discussione da un nuovo instradamento;
  • righe 23-27: si verifica se l’instradamento in corso corrisponde a un ricaricamento della pagina. In caso affermativo, si lascia procedere;
  • a partire dalla riga 29, si riprendono le regole applicate nel componente [navigation] del cliente [nuxt] (cfr. paragrafo precedente);
  • righe 32-38: si gestisce il caso in cui la sessione jSON non sia stata avviata e la destinazione del reindirizzamento non sia la pagina [index]. In questo caso, si reindirizza il browser del cliente alla pagina [index];
  • righe 40-46: si gestisce il caso in cui la sessione jSON sia stata avviata, l’utente non sia autenticato e la destinazione dell’instradamento corrente non sia la pagina [authentification]. In questo caso, si rifiuta l’instradamento e si rimane nella pagina corrente;
  • righe 48-54: si gestisce il caso in cui sia stata avviata la sessione jSON, l’utente sia autenticato e la destinazione dell’instradamento corrente non sia né la pagina [get-admindata] né la pagina [fin-session], che sono quindi le uniche destinazioni possibili. In questo caso, si rifiuta l’instradamento richiesto e si torna al punto precedente;
  • righe 56-62: si gestisce il caso in cui sia stato ottenuto [adminData]. In questo caso, esiste un’unica destinazione possibile per l’instradamento: la pagina [fin-session]. Se non fosse stata questa la pagina richiesta, si rifiuta l’instradamento e si torna al punto precedente;
  • righe 64-72: se si è verificato un reindirizzamento, lo si registra nello store del server [nuxt]: [serverRedirection: true]. Si noti che non viene assegnato alcun valore alla proprietà [from] dello store. Il motivo è che ci sarà un reindirizzamento dal browser client e abbiamo visto che in questo caso non c’è stato alcun instradamento (righe 17-20) e la proprietà [from] dello store non viene utilizzata;
  • righe 66-69: se non c’è reindirizzamento, lo si annota anche nello store del server [nuxt]: [serverRedirection: false]. Inoltre, l’instradamento in corso visualizzerà la pagina [to] che, per la richiesta successiva (cliente o server [nuxt]), diventerà la pagina precedente. Ecco perché si scrive [from: to];
  • righe 73-76: si salva lo store nella sessione [nuxt], a sua volta salvata in un cookie;
  • righe 77-80: se [redirection] non è vuoto, si richiede al browser di reindirizzarsi. Altrimenti (non visibile qui), il ciclo di vita del server [nuxt] proseguirà: la pagina [to] verrà elaborata dal server [nuxt] e inviata al browser del client [nuxt] con il cookie di sessione [nuxt];

Il routing scelto in questo caso per il server [nuxt] è arbitrario. Si sarebbe potuto sceglierne un altro o, come già detto, non effettuarne alcuno. Quello scelto sopra ha il pregio di mantenere sempre l’applicazione in uno stato stabile, indipendentemente dalla pagina URL richiesta dall’utente.

È possibile migliorare ulteriormente la situazione quando la pagina caricata alla fine è quella originale. Si presentano due casi:

  • l’utente ha provocato un ricaricamento della pagina (to===from);
  • vi sono reindirizzamenti alla pagina originale (redirection===from);

In entrambi i casi la pagina originale verrà nuovamente eseguita con la sua chiamata asincrona al server di calcolo delle imposte. Facciamo un esempio. Se, una volta autenticato, l’utente ricarica la pagina (F5). In questo caso, nel percorso sopra indicato, si ha: [to]=[from]=[authentification]. Non c’è alcun reindirizzamento. La pagina [to=authentification] verrà eseguita dal server [nuxt]. Se non si interviene, la funzione [asyncData] verrà eseguita nuovamente. Ciò è superfluo poiché l’autenticazione è già stata effettuata.

È possibile migliorare la situazione modificando leggermente la pagina [authentification]:


// dati asincroni
  async asyncData(context) {
    // log
    console.log('[authentification asyncData started]')
    // non si ripetono le operazioni se la pagina è già stata richiesta
    if (process.server && context.store.state.userAuthenticated) {
      console.log('[authentification asyncData canceled]')
      return { result: '[succès]' }
    }
     // cliente [nuxt]
    if (process.client) {
      // inizio attesa
      context.app.$eventBus().$emit('loading', true)
      // nessun errore
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // si esegue l'autenticazione presso il server
...
  • righe 6-9: se la pagina viene eseguita dal server [nuxt] e si rileva nello store che l’autenticazione è già stata effettuata, allora si restituisce direttamente il risultato desiderato (riga 8);

Si procede allo stesso modo per tutte le pagine:

Pagina [index]:


// dati asincroni
  async asyncData(context) {
    // log
    console.log('[index asyncData started]')
    // non si ripetono le operazioni se la pagina è già stata richiesta
    if (process.server && context.store.state.jsonSessionStarted) {
      console.log('[index asyncData canceled]')
      return { result: '[succès]' }
    }
    try {
...

Pagina [get-admindata]


// dati asincroni
  async asyncData(context) {
    // log
    console.log('[get-admindata asyncData started]')
    // non si esegue l'operazione due volte se la pagina è già stata richiesta
    if (process.server && context.store.state.adminData) {
      console.log('[get-admindata asyncData canceled]')
      return { result: context.store.state.adminData }
    }
    // cliente
    if (process.client) {
      // inizio attesa
      context.app.$eventBus().$emit('loading', true)
      // nessun errore
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   ...  

Pagina [fin-session]


// dati asincroni
  async asyncData(context) {
    // log
    console.log('[fin-session asyncData started]')
    // non si ripetono le operazioni se la pagina è già stata richiesta
    if (process.server && context.store.state.jsonSessionStarted && !context.store.state.userAuthenticated) {
      console.log('[fin-session asyncData canceled]')
      return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
    }
    // caso del cliente [nuxt]
    if (process.client) {
      // inizio attesa
      context.app.$eventBus().$emit('loading', true)
      // nessun errore
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   

16.4. 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

16.5. Conclusion

L’instradamento del server [nuxt] è complesso poiché occorre prevedere tutti i URL che l’utente può digitare manualmente. Si tratta di un caso da manuale. Un’applicazione [nuxt] non è destinata a essere utilizzata in questo modo. Una volta che la pagina [index] è stata fornita dal router del server [nuxt], si potrebbero reindirizzare le successive richieste effettuate al server verso una pagina di errore.

Nel caso specifico del nostro esempio [nuxt-13], l’instradamento del server [nuxt] era superfluo. Quello predefinito (di fatto, assenza di instradamento) nell’esempio [nuxt-12] andava benissimo.