Skip to content

16. Exemplo [nuxt-13]: Verificar a navegação do [nuxt-12]

Neste exemplo, estamos a concentrar-nos na navegação do [nuxt-12]. Não o fizemos no [nuxt-12] porque controlar a navegação teria acrescentado complexidade a um exemplo já de si complexo.

Objetivo: queremos que o utilizador possa realizar apenas ações autorizadas:

  • se a sessão JSON não tiver iniciado, então apenas a URL [/] é permitida;
  • se a sessão JSON tiver começado, mas o utilizador não estiver autenticado, então apenas a URL [/authentication] é permitida;
  • se a sessão JSON tiver iniciado e o utilizador estiver autenticado, então apenas as URLs [/get-admindata, /end-session] são permitidas;
  • quando o destino de encaminhamento atual não for autorizado, será efetuado um redirecionamento para uma URL autorizada;

O exemplo [nuxt-13] é obtido inicialmente através da cópia do exemplo [nuxt-12]:

Image

As alterações serão feitas na pasta de roteamento [middleware].

16.1. Roteamento para a aplicação [nuxt]

O roteamento da aplicação é configurado da seguinte forma no ficheiro [nuxt.config]:


// router
  router: {
    // application URL root
    base: '/nuxt-13/',
    // routing middleware
    middleware: ['routing']
},
  • linha 6: o encaminhamento da aplicação é controlado pelo ficheiro [middleware/routing];

O ficheiro [middleware/routing] é o seguinte:


/* eslint-disable no-console */
 
// on importe les middleware du serveur et du client
import serverRouting from './server/routing'
import clientRouting from './client/routing'
 
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware], process.server', process.server, ', process.client=', process.client)
  if (process.server) {
    // routage serveur
    serverRouting(context)
  } else {
    // routage client
    clientRouting(context)
  }
}
  • linhas 10–16: o encaminhamento do cliente e do servidor [nuxt] são tratados de forma diferente. Este é um ponto de diferença importante entre eles;
  • linha 4: o encaminhamento do servidor é implementado pelo script [middleware/server/routing];
  • linha 5: o encaminhamento do cliente é implementado pelo script [middleware/client/routing];

16.2. Roteamento do cliente [nuxt]

O encaminhamento do lado do cliente [nuxt] permanece o mesmo que era no [nuxt-12]:


/* eslint-disable no-console */
export default function(context) {
  // who executes this code?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // management of the PHP session cookie in the browser
  // the browser's PHP session cookie must be identical to the one found in the nuxt session
  // acion [fin-session] receives a new cookie PHP (server as nuxt client)
  // if the server receives it, the client must pass it on to the browser
  // for its own exchanges with the PHP server
  // this is customer routing
 
  // retrieve the session cookie PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // if it exists, we assign the PHP session cookie to the browser
    document.cookie = phpSessionCookie
  }
 
  ...
}

Para impedir que o cliente aceda a percursos não autorizados, basta disponibilizar apenas os percursos autorizados no menu de navegação do cliente. O componente [components/navigation] passa a ter o seguinte aspeto:


<template>
  <!-- bootstrap menu with three options -->
  <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>
  • linha 4: a opção [Authentication] só está disponível se a sessão JSON tiver sido iniciada, mas o utilizador não estiver autenticado. Se a sessão JSON não tiver sido iniciada ou se o utilizador já estiver autenticado, a opção não estará disponível;
  • Linhas 7–11: a opção [AdminData Request] só está disponível se a sessão JSON tiver iniciado, o utilizador estiver autenticado e os dados [AdminData] ainda não tiverem sido recuperados. Se alguma destas três condições não for cumprida (sessão JSON não iniciada, utilizador não autenticado ou dados [AdminData] já recuperados), a opção não é apresentada;
  • linha 15: a opção [End Tax Session] está disponível assim que a sessão JSON tiver sido iniciada e o utilizador estiver autenticado; caso contrário, não está disponível;

16.3. Roteamento do servidor [Nuxt]

O encaminhamento do servidor é geralmente mais complexo do que o encaminhamento do lado do cliente, porque o utilizador pode digitar qualquer URL na barra de endereços do navegador. Podemos deixar isso acontecer (afinal, o utilizador não deve fazer isso) ou tentar controlá-lo. É isso que faremos aqui, para efeitos deste exemplo, porque no caso da aplicação [nuxt-12], podemos facilmente passar sem isso, uma vez que o servidor de cálculo de impostos está bem protegido contra estas URLs introduzidas manualmente e sabe como enviar as mensagens de erro apropriadas. Vimos isto em [next-12], onde não havia controlo de encaminhamento.

O encaminhamento num servidor [nuxt] é muito diferente do de um cliente [nuxt] em termos de redirecionamento:

  • quando um servidor [nuxt] é redirecionado, ele envia um pedido de redirecionamento para o navegador do cliente com o destino do redirecionamento. O navegador, então, faz um novo pedido ao servidor [nuxt], solicitando o destino que lhe foi enviado. É como se o utilizador tivesse digitado manualmente o URL do destino do redirecionamento: toda a aplicação [nuxt] reinicia e, com ela, todo o seu ciclo de vida (plugins de servidor, store, roteamento de servidor, páginas);
  • quando um cliente [nuxt] é redirecionado, nada disso acontece. Há simplesmente uma mudança de página, tal como teria ocorrido se o utilizador tivesse clicado num link que conduz ao destino do redirecionamento. O ciclo de vida é então diferente (roteamento do lado do cliente, renderização do destino da rota);

Por esta razão, é preferível separar o encaminhamento do lado do cliente do encaminhamento do lado do servidor, mesmo que as duas bases de código possam parecer semelhantes.

O script de roteamento do servidor [middleware/server/routing] será o seguinte:


/* eslint-disable no-console */
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)
 
  // on récupère quelques informations dans le store [nuxt]
  const store = context.store
  // d'où vient-on ?
  const from = store.state.from || 'nowhere'
  ...
}
  • No roteamento do lado do cliente, a função de roteamento recebe o contexto [context] com a propriedade [context.from], que é a rota da página de onde viemos. A rota para onde estamos indo é obtida através de [context.route];
  • No roteamento do lado do servidor, a função de roteamento recebe o contexto [context] sem a propriedade [context.from]. O roteamento do lado do servidor só ocorre quando uma URL é solicitada manualmente ao servidor [nuxt]. Sabemos que toda a aplicação [nuxt] é então reiniciada. É como se estivéssemos a começar do zero, pelo que não existe o conceito de uma «página anterior»;
  • graças à sessão [nuxt], sabemos que o servidor pode recuperar esta sessão e, assim, evitar começar do zero. É, portanto, nesta sessão [nuxt], e mais especificamente no armazenamento da sessão, que iremos guardar o nome da última página exibida pelo navegador do cliente antes de uma URL ser solicitada ao servidor [nuxt];
  • Linhas 7–9: Recuperamos o nome da última página exibida pelo navegador do cliente. Quando a aplicação é iniciada, esta informação [from] não existe no armazenamento. Atribuímos então o nome [nowhere] à variável [from];

Para que o servidor [nuxt] possa recuperar do store o nome da última página apresentada pelo navegador do cliente, o cliente [nuxt] também deve inserir essa informação no store. O script de encaminhamento do cliente [nuxt] fica, portanto, completo da seguinte forma:


/* eslint-disable no-console */
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // gestion du cookie de la session PHP dans le navigateur
  // le cookie de la session PHP du navigateur doit être identique à celui trouvé en session nuxt
  // l'acion [fin-session] reçoit un nouveau cookie PHP (serveur comme client nuxt)
  // si c'est le serveur qui le reçoit, le client doit le transmettre au navigateur
  // pour ses propres échanges avec le serveur PHP
  // on est ici dans un routing client
 
  // on récupère le cookie de la session PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // s'il existe, on affecte le cookie de session PHP au navigateur
    document.cookie = phpSessionCookie
  }
 
  // on met dans la session le nom de la page où on va - pas de redirection serveur
  context.store.commit('replace', { serverRedirection: false, from: context.route.name })
  // on sauvegarde le store dans la session [nuxt]
  const session = context.app.$session()
  session.value.store = context.store.state
  session.save(context)
}
  • São adicionadas as linhas 19–24;
  • Linha 20: Armazenamos o nome da página [context.route.name] que será exibida na loja, que servirá então como a página de onde viemos durante o próximo passo de roteamento. Além disso, veremos que, no roteamento do servidor [nuxt], ele precisa saber se o roteamento atual decorre de um redirecionamento anterior pelo servidor [nuxt]. Aqui, esse não é o caso, por isso definimos a propriedade [serverRedirection] como [false];
  • linhas 22–24: o estado do store é colocado na sessão [nuxt] (linha 23), depois a sessão [nuxt] é guardada num cookie (linha 24), que por sua vez será guardado no navegador do cliente [nuxt];

Voltemos ao script de roteamento do servidor [nuxt]:


/* eslint-disable no-console */
export default function(context) {
  // qui exécute ce code ?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)
 
  // on récupère quelques informations dans le store [nuxt]
  const store = context.store
  // d'où vient-on ?
  const from = store.state.from || 'nowhere'
  // où va-t-on ?
  const to = context.route.name
  // éventuelle redirection
  let redirection = ''
  // gestion du routage terminé
  let done = false
 
  // est-on déjà dans une redirection du serveur [nuxt]?
  if (store.state.serverRedirection) {
    // rien à faire
    done = true
  }
 
  // est-ce un rechargement de page ?
  if (to === from) {
    // rien à faire
    done = true
  }
 
  // contrôle de la navigation du serveur [nuxt]
  // on s'inspire de la navigation client dans le composant [navigation]
 
  // cas où la session PHP n'a pas démarré
  if (!done && !store.state.jsonSessionStarted && to !== 'index') {
    // redirection
    redirection = 'index'
    // travail terminé
    done = true
  }
 
  // cas où l'utilisateur n'est pas authentifié
  if (!done && store.state.jsonSessionStarted && !store.state.userAuthenticated && to !== 'authentification') {
    // redirection
    redirection = from
    // travail terminé
    done = true
  }
 
  // cas où l'utilisateur a été authentifié
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && to !== 'get-admindata' && to !== 'fin-session') {
    // on reste sur la même page
    redirection = from
    // travail terminé
    done = true
  }
 
  // cas où [adminData] a été obtenu
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && store.state.adminData && to !== 'fin-session') {
    // on reste sur la même page
    redirection = from
    // travail terminé
    done = true
  }
 
  // on a fait tous les contrôles ---------------------
  // redirection ?
  if (redirection) {
    // on note la redirection dans le store
    store.commit('replace', { serverRedirection: true })
  } else {
    // pas de redirection
    store.commit('replace', { serverRedirection: false, from: to })
  }
  // on sauvegarde le store dans la session [nuxt]
  const session = context.app.$session()
  session.value.store = store.state
  session.save(context)
  // on fait l'éventuelle redirection
  if (redirection) {
    context.redirect({ name: redirection })
  }
}
  • linhas 6–9: recuperam o valor de [from] do armazenamento do servidor [nuxt];
  • linha 11: registamos o destino da rota atual;
  • linha 13: o encaminhamento pode resultar num redirecionamento do navegador do cliente. [redirection] será o destino desse redirecionamento;
  • linha 15: [done] definido como [true] indica que o encaminhamento está concluído;
  • linhas 17–21: Primeiro, verificamos se o encaminhamento atual resultou de um pedido de redirecionamento enviado para o navegador do cliente. Esta informação está armazenada na propriedade [serverRedirection] do armazenamento. Se esta propriedade for verdadeira, então o servidor [nuxt] enviou um redirecionamento para o navegador do cliente durante o pedido anterior ao servidor [nuxt]. Neste caso, não é necessário qualquer encaminhamento. Durante a solicitação anterior, o roteador do servidor [nuxt] decidiu que o navegador do cliente deveria ser redirecionado. Essa decisão não precisa ser substituída por um novo roteamento;
  • linhas 23–27: verificamos se o encaminhamento atual é uma recarga da página. Se for, deixamos que prossiga;
  • A partir da linha 29, retomamos as regras aplicadas no componente [navigation] do cliente [nuxt] (ver parágrafo anterior);
  • linhas 32–38: tratamos o caso em que a sessão JSON não foi iniciada e o destino do roteamento não é a página [index]. Neste caso, redirecionamos o navegador do cliente para a página [index];
  • linhas 40–46: tratamos o caso em que a sessão JSON já começou, o utilizador não está autenticado e o destino do encaminhamento atual não é a página [authentication]. Neste caso, rejeitamos o encaminhamento e permanecemos onde estávamos;
  • linhas 48–54: trata do caso em que a sessão JSON já começou, o utilizador está autenticado e o destino de encaminhamento atual não é nem a página [get-admindata] nem a página [end-session], que são então os únicos destinos possíveis. Neste caso, o encaminhamento solicitado é rejeitado e voltamos para onde estávamos anteriormente;
  • linhas 56–62: tratamos o caso em que [adminData] foi obtido. Neste caso, existe apenas um destino de encaminhamento possível: a página [fin-session]. Se essa não fosse a página solicitada, rejeitamos o encaminhamento e voltamos para onde estávamos anteriormente;
  • linhas 64–72: se ocorreu um redirecionamento, registamo-lo no armazenamento do servidor [nuxt]: [serverRedirection: true]. Note que não atribuímos um valor à propriedade [from] do armazenamento. A razão é que haverá um redirecionamento a partir do navegador do cliente e vimos que, neste caso, não houve encaminhamento (linhas 17–20) e a propriedade [from] do armazenamento não é utilizada;
  • Linhas 66–69: Se não houver redirecionamento, isso também é anotado no armazenamento do servidor [nuxt]: [serverRedirection: false]. Além disso, o roteamento atual exibirá a página [to], que para a próxima solicitação (cliente ou servidor [nuxt]) se tornará a página anterior. É por isso que escrevemos [from: to];
  • linhas 73–76: Guardamos o store na sessão [nuxt], que por sua vez é guardada num cookie;
  • linhas 77–80: se [redirection] não estiver vazio, instruímos o navegador a redirecionar. Caso contrário (não mostrado aqui), o ciclo de vida do servidor [nuxt] continuará: a página [to] será processada pelo servidor [nuxt] e enviada para o navegador do cliente [nuxt] juntamente com o cookie de sessão [nuxt];

O roteamento escolhido aqui para o servidor [nuxt] é arbitrário. Poderíamos ter escolhido outro ou, como mencionado, não ter usado roteamento algum. O escolhido acima tem a vantagem de manter sempre a aplicação num estado estável, independentemente da URL solicitada pelo utilizador.

Podemos melhorar ligeiramente isto quando a página que é carregada no final é a página original. Existem dois casos:

  • o utilizador tenha acionado uma atualização da página (to===from);
  • existem redirecionamentos para a página original (redirecionamento===de);

Em ambos os casos, a página original será executada novamente com a sua chamada assíncrona para o servidor de cálculo de impostos. Vejamos um exemplo. Se, uma vez autenticado, o utilizador recarregar a página (F5). Neste caso, no roteamento acima, temos: [to]=[from]=[authentication]. Não há redirecionamento. A página [para=autenticação] será executada pelo servidor [nuxt]. Se não fizermos nada, a função [asyncData] será executada novamente. Isto é desnecessário, uma vez que a autenticação já foi realizada.

Podemos melhorar isto modificando ligeiramente a página [authentication]:


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[authentification asyncData started]')
    // don't do things twice if the page has already been requested
    if (process.server && context.store.state.userAuthenticated) {
      console.log('[authentification asyncData canceled]')
      return { result: '[succès]' }
    }
      // customer [nuxt]
    if (process.client) {
      // start waiting
      context.app.$eventBus().$emit('loading', true)
      // no error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // authenticate to the server
...
  • linhas 6-9: se a página estiver a ser servida pelo servidor [nuxt] e verificarmos no store que a autenticação já foi realizada, então devolvemos o resultado desejado diretamente (linha 8);

Fazemos o mesmo para todas as páginas:

Página [index]:


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[index asyncData started]')
    // don't do things twice if the page has already been requested
    if (process.server && context.store.state.jsonSessionStarted) {
      console.log('[index asyncData canceled]')
      return { result: '[succès]' }
    }
    try {
...

Página [get-admindata]


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[get-admindata asyncData started]')
    // don't do things twice if the page has already been requested
    if (process.server && context.store.state.adminData) {
      console.log('[get-admindata asyncData canceled]')
      return { result: context.store.state.adminData }
    }
    // customer
    if (process.client) {
      // start waiting
      context.app.$eventBus().$emit('loading', true)
      // no error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   ...  

Página [fim da sessão]


// asynchronous data
  async asyncData(context) {
    // log
    console.log('[fin-session asyncData started]')
    // don't do things twice if the page has already been requested
    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)." }
    }
    // customer case [nuxt]
    if (process.client) {
      // start waiting
      context.app.$eventBus().$emit('loading', true)
      // no error
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
 

16.4. Execução

Para executar este exemplo, certifique-se de que apaga o cookie de sessão [nuxt] e o cookie PHP do navegador que está a executar o cliente [nuxt] antes da execução, para começar do zero. Segue-se um exemplo utilizando o navegador Chrome:

Image

16.5. Conclusão

O encaminhamento no servidor [nuxt] é complexo porque é necessário antecipar todos os URLs que o utilizador possa digitar manualmente. Este é um exemplo clássico. Uma aplicação [nuxt] não se destina a ser utilizada desta forma. Assim que a página [index] for servida pelo encaminhador do servidor [nuxt], os pedidos subsequentes feitos ao servidor poderão ser redirecionados para uma página de erro.

No caso específico do nosso exemplo [nuxt-13], o encaminhamento no servidor [nuxt] era desnecessário. O comportamento padrão (essencialmente sem encaminhamento) no exemplo [nuxt-12] funcionou perfeitamente.