Skip to content

16. Exemplo [nuxt-13]: verificação da navegação de [nuxt-12]

Neste exemplo, estamos interessados na navegação do [nuxt-12]. Não o fizemos no [nuxt-12] porque a verificação da navegação teria complicado um exemplo já de si complexo.

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

  • se a sessão jSON não tiver sido iniciada, então apenas a sessão URL e a sessão [/] são autorizadas;
  • se a sessão jSON tiver sido iniciada, mas o utilizador não estiver autenticado, então apenas as sessões URL e [/authentification] são autorizadas;
  • se a sessão jSON tiver sido iniciada e o utilizador estiver autenticado, então apenas as sessões URL e [/get-admindata, /fin-session] são autorizadas;
  • quando o destino do encaminhamento atual não for autorizado, proceder-se-á a um redirecionamento para uma URL autorizada;

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

Image

É na pasta de encaminhamento [middleware] que serão efetuadas as alterações.

16.1. Roteamento da aplicação [nuxt]

O encaminhamento da aplicação está configurado da seguinte forma no ficheiro [nuxt.config]:


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

O ficheiro [middleware/routing] é o seguinte:


/* eslint-disable no-console */

// importamos os middlewares do servidor e do cliente
import serverRouting from './server/routing'
import clientRouting from './client/routing'

export default function(context) {
  // quem executa este código?
  console.log('[middleware], process.server', process.server, ', process.client=', process.client)
  if (process.server) {
    // roteamento do servidor
    serverRouting(context)
  } else {
    // roteamento do cliente
    clientRouting(context)
  }
}
  • linhas 10-16: o encaminhamento do cliente e do servidor [nuxt] é tratado de forma diferente. Este é um ponto em que diferem significativamente;
  • 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 cliente [nuxt] permanece tal como estava no [nuxt-12]:


/* eslint-disable no-console */
export default function(context) {
  // quem executa este código?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // gestão do cookie de sessão PHP no navegador
  // o cookie de sessão PHP do navegador deve ser idêntico ao encontrado na sessão Nuxt
  // a ação [fin-session] recebe um novo cookie PHP (tanto no servidor como no cliente Nuxt)
  // se for o servidor a recebê-lo, o cliente deve transmiti-lo ao navegador
  // para as suas próprias comunicações com o servidor PHP
  // estamos aqui num encaminhamento do cliente

  // recuperamos o cookie da sessão PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // se existir, atribui-se o cookie de sessão PHP ao navegador
    document.cookie = phpSessionCookie
  }

  ...
}

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


<template>
  <!-- menu Bootstrap com três opções -->
  <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 [Authentification] só é apresentada 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 é apresentada;
  • linhas 7-11: a opção [Requête AdminData] só é disponibilizada se a sessão jSON tiver sido iniciada, se o utilizador estiver autenticado e se os dados [AdminData] ainda não tiverem sido recuperados. Se uma destas três condições não for satisfeita (sessão jSON não iniciada, utilizador não autenticado ou os dados [AdminData] já recuperados), a opção não é disponibilizada;
  • linha 15: a opção [Fin session impôt] é disponibilizada assim que a sessão jSON for iniciada e o utilizador estiver autenticado; caso contrário, não é disponibilizada;

16.3. Roteamento do servidor [nuxt]

O encaminhamento do servidor é, em geral, mais complexo do que o do cliente, pois o utilizador pode digitar qualquer URL na barra de endereços do seu navegador. Podemos deixar isso acontecer (afinal, o utilizador não deve fazer isso) ou tentar controlar a situação. É isso que vamos fazer aqui, a título de exemplo, pois no caso da aplicação [nuxt-12], podemos muito bem dispensar esse controlo, uma vez que o servidor de cálculo de impostos está bem protegido contra esses URL introduzidos manualmente e sabe enviar as mensagens de erro adequadas. Vimos isso no [next-12], onde não havia qualquer controlo de encaminhamento.

O encaminhamento de um servidor [nuxt] é muito diferente do de um cliente [nuxt] no que diz respeito ao conceito de redirecionamento:

  • quando um servidor [nuxt] é redirecionado, envia uma ordem de redirecionamento ao navegador do cliente com o destino do redirecionamento. O navegador faz então uma nova solicitação ao servidor [nuxt], pedindo-lhe o destino que lhe foi transmitido. Tudo acontece como se o utilizador tivesse digitado manualmente o endereço URL do destino da redireção: toda a aplicação [nuxt] é reiniciada e, consequentemente, todo o seu ciclo de vida (plugins do servidor, armazenamento, encaminhamento do servidor, páginas);
  • quando um cliente [nuxt] é redirecionado, nada disso acontece. Ocorre uma simples mudança de página, a mesma que teria ocorrido se o utilizador tivesse clicado num link que levasse ao destino da redireção. O ciclo de vida é, então, diferente (roteamento do cliente, exibição do destino da rota);

Por esta razão, é preferível separar o encaminhamento do cliente do encaminhamento do servidor, mesmo que os dois códigos possam parecer semelhantes.

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


/* eslint-disable no-console */
export default function(context) {
  // quem executa este código?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)

  // recuperamos algumas informações do store [nuxt]
  const store = context.store
  // de onde viemos?
  const from = store.state.from || 'nowhere'
  ...
}
  • no encaminhamento do cliente, a função de encaminhamento recebe o contexto [context] com a propriedade [context.from], que é a rota da página de onde se vem. A rota para onde se vai é obtida por [context.route];
  • no encaminhamento do servidor, a função de encaminhamento recebe o contexto [context] sem a propriedade [context.from]. O encaminhamento do servidor só intervém quando um URL é solicitado manualmente ao servidor [nuxt]. Sabe-se que, nessa altura, toda a aplicação [nuxt] é reiniciada. É como se se recomeçasse do zero e, por isso, não existe a noção de «página anterior»;
  • graças à sessão [nuxt], sabemos que o servidor pode recuperar essa sessão e, assim, não recomeçar do zero. É, portanto, nesta sessão [nuxt] e, mais especificamente, no armazenamento desta sessão, que iremos guardar o nome da última página apresentada pelo navegador do cliente antes de uma URL ser solicitada ao servidor [nuxt];
  • linhas 7-9: recuperamos o nome da última página apresentada pelo navegador do cliente. Ao iniciar a aplicação, esta informação [from] não existe no armazenamento. Atribui-se então o nome [nowhere] à variável [from];

Para que o servidor [nuxt] possa recuperar do armazenamento o nome da última página apresentada pelo navegador do cliente, é necessário que o cliente [nuxt] também coloque essa informação no armazenamento. O script de encaminhamento do cliente [nuxt] é, portanto, completado da seguinte forma:


/* eslint-disable no-console */
export default function(context) {
  // Quem está a executar este código?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // gestão do cookie de sessão PHP no navegador
  // O cookie de sessão PHP do navegador deve ser idêntico ao encontrado na sessão Nuxt
  // a ação [fin-session] recebe um novo cookie PHP (tanto no servidor como no cliente Nuxt)
  // se for o servidor a recebê-lo, o cliente deve transmiti-lo ao navegador
  // para as suas próprias comunicações com o servidor PHP
  // estamos aqui num encaminhamento do cliente

  // recuperamos o cookie da sessão PHP
  const phpSessionCookie = context.store.state.phpSessionCookie
  if (phpSessionCookie) {
    // se existir, atribui-se o cookie de sessão PHP ao navegador
    document.cookie = phpSessionCookie
  }

  // coloca-se na sessão o nome da página para onde se vai - sem redirecionamento do servidor
  context.store.commit('replace', { serverRedirection: false, from: context.route.name })
  // guardamos o store na sessão [nuxt]
  const session = context.app.$session()
  session.value.store = context.store.state
  session.save(context)
}
  • são adicionadas as linhas 19-24;
  • linha 20: insere-se na memória o nome da página [context.route.name] que vai ser apresentada e que será, assim, no encaminhamento seguinte, a página de onde se vem. Além disso, veremos que, no encaminhamento do servidor [nuxt], este precisa de saber se o encaminhamento em curso resulta de um redirecionamento anterior do servidor [nuxt]. Neste caso, não é esse o caso, pelo que definimos a propriedade [serverRedirection] como [false];
  • linhas 22-24: o estado do armazenamento é colocado na sessão [nuxt] (linha 23) e, em seguida, a sessão [nuxt] é guardada num cookie (linha 24), que, por sua vez, será guardado no navegador do cliente [nuxt];

Voltemos ao script de encaminhamento do servidor [nuxt]:


/* eslint-disable no-console */
export default function(context) {
  // Quem executa este código?
  console.log('[middleware server], process.server', process.server, ', process.client=', process.client)

  // recuperamos algumas informações do armazenamento [nuxt]
  const store = context.store
  // de onde viemos?
  const from = store.state.from || 'nowhere'
  // Para onde vamos?
  const to = context.route.name
  // eventual redirecionamento
  let redirection = ''
  // gestão do encaminhamento concluída
  let done = false

  // já estamos num redirecionamento do servidor [nuxt]?
  if (store.state.serverRedirection) {
    // não há nada a fazer
    done = true
  }

  // trata-se de uma atualização da página?
  if (to === from) {
    // não há nada a fazer
    done = true
  }
  
  // controlo da navegação do servidor [nuxt]
  // baseia-se na navegação do cliente no componente [navigation]

  // caso em que a sessão PHP não tenha sido iniciada
  if (!done && !store.state.jsonSessionStarted && to !== 'index') {
    // redirecionamento
    redirection = 'index'
    // trabalho concluído
    done = true
  }

  // caso em que o utilizador não esteja autenticado
  if (!done && store.state.jsonSessionStarted && !store.state.userAuthenticated && to !== 'authentification') {
    // redirecionamento
    redirection = from
    // trabalho concluído
    done = true
  }

  // caso em que o utilizador foi autenticado
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && to !== 'get-admindata' && to !== 'fin-session') {
    // permanece-se na mesma página
    redirection = from
    // trabalho concluído
    done = true
  }

  // caso em que [adminData] foi obtido
  if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && store.state.adminData && to !== 'fin-session') {
    // permanece-se na mesma página
    redirection = from
    // trabalho concluído
    done = true
  }

  // foram realizadas todas as verificações ---------------------
  // redirecionamento?
  if (redirection) {
    // regista-se o redirecionamento na loja
    store.commit('replace', { serverRedirection: true })
  } else {
    // sem redirecionamento
    store.commit('replace', { serverRedirection: false, from: to })
  }
  // guardamos o armazenamento na sessão [nuxt]
  const session = context.app.$session()
  session.value.store = store.state
  session.save(context)
  // é efetuado o eventual redirecionamento
  if (redirection) {
    context.redirect({ name: redirection })
  }
}
  • linhas 6-9: recupera-se o valor de [from] na memória do servidor [nuxt];
  • linha 11: regista-se o destino do encaminhamento atual;
  • linha 13: o encaminhamento pode levar a um redirecionamento do navegador do cliente. [redirection] será o destino desse redirecionamento;
  • linha 15: [done] para [true] indica que o encaminhamento está concluído;
  • linhas 17-21: verifica-se primeiro se o encaminhamento atual resulta de um pedido de redirecionamento enviado ao navegador do cliente. Esta informação está armazenada na propriedade [serverRedirection] do armazenamento. Se esta propriedade for verdadeira, significa que o servidor [nuxt] enviou um redirecionamento para o navegador do cliente durante a solicitação anterior ao servidor [nuxt]. Neste caso, não há encaminhamento a efetuar. Na solicitação anterior, o encaminhador do servidor [nuxt] decidiu que o navegador do cliente deveria ser redirecionado. Esta decisão não deve ser posta em causa por um novo encaminhamento;
  • linhas 23-27: verifica-se se o encaminhamento em curso corresponde a uma atualização da página. Se for o caso, deixa-se que o processo siga o seu curso;
  • a partir da linha 29, retomam-se as regras aplicadas no componente [navigation] do cliente [nuxt] (ver parágrafo anterior);
  • linhas 32-38: trata-se do caso em que a sessão jSON não foi iniciada e o destino do encaminhamento não é a página [index]. Neste caso, redireciona-se o navegador do cliente para a página [index];
  • linhas 40-46: trata-se do caso em que a sessão jSON foi iniciada, o utilizador não está autenticado e o destino do encaminhamento atual não é a página [authentification]. Neste caso, o encaminhamento é recusado e permanece-se na página atual;
  • linhas 48-54: tratamos o caso em que a sessão jSON foi iniciada, o utilizador está autenticado e o destino do encaminhamento atual não é nem a página [get-admindata], nem a página [fin-session], que são, nesse momento, os únicos destinos possíveis. Neste caso, rejeita-se o encaminhamento solicitado e regressa-se ao ponto em que se estava anteriormente;
  • linhas 56-62: trata-se do caso em que se obteve [adminData]. Neste caso, existe apenas um destino possível para o encaminhamento: a página [fin-session]. Se não fosse esta a página solicitada, rejeita-se o encaminhamento e regressa-se ao ponto anterior;
  • linhas 64-72: se tiver havido redirecionamento, regista-se no armazenamento do servidor [nuxt]: [serverRedirection: true]. Note-se que não se atribui qualquer valor à propriedade [from] do armazenamento. A razão para isso é que vai haver um redirecionamento 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, então isso também é registado no armazenamento do servidor [nuxt]: [serverRedirection: false]. Além disso, o encaminhamento em curso irá apresentar a página [to], que, para o pedido seguinte (cliente ou servidor [nuxt]), se tornará a página anterior. É por isso que se escreve [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, solicita-se ao navegador que se redirecione. Caso contrário (o que não se vê 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] com o cookie de sessão [nuxt];

O encaminhamento aqui escolhido para o servidor [nuxt] é arbitrário. Poderia ter-se escolhido outro ou, como já foi referido, não se poderia ter feito qualquer encaminhamento. O escolhido acima tem a vantagem de manter sempre a aplicação num estado estável, independentemente da página URL solicitada pelo utilizador.

É possível melhorar um aspeto quando a página carregada no final é a página original. Existem dois casos:

  • o utilizador provocou uma recarga da página (to===from);
  • existem redirecionamentos para a página original (redirecionamento===from);

Em ambos os casos, a página original será novamente executada com a sua chamada assíncrona ao servidor de cálculo do imposto. Vejamos um exemplo. Se, uma vez autenticado, o utilizador recarregar a página (F5). Neste caso, no roteamento acima, temos: [to]=[from]=[authentification]. Não há redirecionamento. A página [to=authentification] será executada pelo servidor [nuxt]. Se não for tomada nenhuma medida, a função [asyncData] será executada novamente. Isso é desnecessário, uma vez que a autenticação já foi efetuada.

É possível melhorar a situação alterando ligeiramente a página [authentification]:


// dados assíncronos
  async asyncData(context) {
    // registo
    console.log('[authentification asyncData started]')
    // não se repetem as operações se a página já tiver sido solicitada
    if (process.server && context.store.state.userAuthenticated) {
      console.log('[authentification asyncData canceled]')
      return { result: '[succès]' }
    }
     // cliente [nuxt]
    if (process.client) {
      // início da espera
      context.app.$eventBus().$emit('loading', true)
      // sem erros
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
      // autenticação no servidor
...
  • linhas 6-9: se a página for executada pelo servidor [nuxt] e se for detetado na memória que a autenticação já foi efetuada, então devolve-se diretamente o resultado pretendido (linha 8);

Faz-se o mesmo para todas as páginas:

Página [index]:


// dados assíncronos
  async asyncData(context) {
    // registo
    console.log('[index asyncData started]')
    // não se repete o processo se a página já tiver sido solicitada
    if (process.server && context.store.state.jsonSessionStarted) {
      console.log('[index asyncData canceled]')
      return { result: '[succès]' }
    }
    try {
...

Página [get-admindata]


// dados assíncronos
  async asyncData(context) {
    // registo
    console.log('[get-admindata asyncData started]')
    // não se faz as coisas duas vezes se a página já tiver sido solicitada
    if (process.server && context.store.state.adminData) {
      console.log('[get-admindata asyncData canceled]')
      return { result: context.store.state.adminData }
    }
    // cliente
    if (process.client) {
      // início da espera
      context.app.$eventBus().$emit('loading', true)
      // sem erros
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   ...  

Página [fin-session]


// dados assíncronos
  async asyncData(context) {
    // registo
    console.log('[fin-session asyncData started]')
    // não se repete o processo se a página já tiver sido solicitada
    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 do cliente [nuxt]
    if (process.client) {
      // início da espera
      context.app.$eventBus().$emit('loading', true)
      // sem erros
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   

16.4. Exécution

Para executar este exemplo, é necessário, antes da execução, eliminar o cookie de sessão [nuxt] e o cookie PHP do navegador que está a executar o cliente [nuxt], de modo a partir de uma situação limpa. Segue-se um exemplo com o navegador Chrome:

Image

16.5. Conclusion

O encaminhamento do servidor [nuxt] é complexo, pois é necessário prever todos os URL que o utilizador possa digitar manualmente. Trata-se de um caso clássico. Uma aplicação [nuxt] não se destina a ser utilizada desta forma. Assim que a página [index] for servida pelo router do servidor [nuxt], seria possível redirecionar as chamadas seguintes feitas ao servidor para uma página de erro.

No caso específico do nosso exemplo [nuxt-13], o encaminhamento do servidor [nuxt] era desnecessário. O encaminhamento por predefinição (ou seja, a ausência de encaminhamento) no exemplo [nuxt-12] era perfeitamente adequado.