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 fizemos isso no [nuxt-12] porque a verificação da navegação teria complicado ainda mais um exemplo que já era complexo.

Objetivo: queremos que o usuário só possa realizar ações autorizadas:

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

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

Image

É na pasta de roteamento [middleware] que as modificações serão realizadas.

16.1. Roteamento do aplicativo [nuxt]

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


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

O arquivo [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 esse 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 roteamento do cliente e do servidor [nuxt] é tratado de maneira diferente. Esse é um ponto em que eles diferem significativamente;
  • linha 4: o roteamento do servidor é implementado pelo script [middleware/server/routing];
  • linha 5: o roteamento do cliente é implementado pelo script [middleware/client/routing];

16.2. Roteamento do cliente [nuxt]

O roteamento do cliente [nuxt] permanece igual ao que era no [nuxt-12]:


/* eslint-disable no-console */
export default function(context) {
  // quem executa esse código?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // gerenciamento 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 do Nuxt
  // a ação [fin-session] recebe um novo cookie PHP (tanto no servidor quanto no cliente Nuxt)
  // se for o servidor que o receber, o cliente deve transmiti-lo ao navegador
  // para suas próprias comunicações com o servidor PHP
  // estamos aqui em um roteamento do cliente

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

  ...
}

Para evitar que o cliente acesse rotas não autorizadas, basta oferecer a ele, no menu de navegação do cliente, apenas as rotas autorizadas. O componente [components/navigation] passa a ser o seguinte:


<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ó é oferecida se a sessão jSON tiver sido iniciada, mas o usuário não estiver autenticado. Se a sessão jSON não tiver sido iniciada ou se o usuário já estiver autenticado, a opção não será oferecida;
  • linhas 7-11: a opção [Requête AdminData] só é oferecida se a sessão jSON tiver sido iniciada, se o usuário estiver autenticado e se o dado [AdminData] ainda não tiver sido recuperado. Se uma dessas três condições não for satisfeita (sessão jSON não iniciada, usuário não autenticado ou os dados [AdminData] já recuperados), a opção não é oferecida;
  • linha 15: a opção [Fin session impôt] é oferecida assim que a sessão jSON for iniciada e o usuário estiver autenticado; caso contrário, ela não é oferecida;

16.3. Roteamento do servidor [nuxt]

O roteamento do servidor é, em geral, mais complexo do que o do cliente, pois o usuário pode digitar qualquer URL na barra de endereços do navegador. Podemos deixar isso acontecer (afinal, o usuário não deveria fazer isso) ou tentar controlar a situação. É isso que faremos aqui, a título de exemplo, pois, no caso do aplicativo [nuxt-12], podemos muito bem dispensar esse controle, já que o servidor de cálculo de impostos está bem protegido contra esses URL inseridos manualmente e sabe enviar as mensagens de erro adequadas. Vimos isso no [next-12], onde não havia nenhum controle de roteamento.

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

  • quando um servidor [nuxt] é redirecionado, ele envia uma ordem de redirecionamento ao navegador do cliente com o destino do redirecionamento. O navegador, então, faz uma nova solicitação ao servidor [nuxt], solicitando o destino que lhe foi transmitido. Tudo ocorre como se o usuário tivesse digitado manualmente o endereço URL do destino do redirecionamento: todo o aplicativo [nuxt] é reiniciado e, consequentemente, todo o seu ciclo de vida (plug-ins do servidor, store, roteamento do servidor, páginas);
  • quando um cliente [nuxt] é redirecionado, nada disso ocorre. Há uma simples mudança de página, a mesma que teria ocorrido se o usuário tivesse clicado em um link que levasse ao destino do redirecionamento. O ciclo de vida é, então, diferente (roteamento do cliente, exibição do destino da rota);

Por esse motivo, é preferível separar o roteamento do cliente do roteamento do servidor, mesmo que os dois códigos possam parecer semelhantes.

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


/* eslint-disable no-console */
export default function(context) {
  // quem está executando esse 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 roteamento do cliente, a função de roteamento 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 roteamento do servidor, a função de roteamento recebe o contexto [context] sem a propriedade [context.from]. O roteamento do servidor só intervém quando um URL é solicitado manualmente ao servidor [nuxt]. Sabe-se que, nesse caso, toda a aplicação [nuxt] é reinicializada. É como se começássemos do zero e, portanto, não há o conceito de “página anterior”;
  • graças à sessão [nuxt], sabemos que o servidor pode recuperar essa sessão e, assim, não precisar recomeçar do zero. Portanto, é nessa sessão [nuxt] e, mais especificamente, no armazenamento dessa sessão, que armazenaremos o nome da última página exibida pelo navegador do cliente antes que uma URL seja solicitada ao servidor [nuxt];
  • linhas 7-9: recuperamos o nome da última página exibida pelo navegador do cliente. Ao iniciar o aplicativo, essa informação [from] não existe no armazenamento. Em seguida, atribui-se o nome [nowhere] à variável [from];

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


/* eslint-disable no-console */
export default function(context) {
  // quem está executando esse código?
  console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
  // Gerenciamento 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 do Nuxt
  // a ação [fin-session] recebe um novo cookie PHP (tanto no servidor quanto no cliente Nuxt)
  // se for o servidor que o receber, o cliente deve transmiti-lo ao navegador
  // para suas próprias comunicações com o servidor PHP
  // estamos aqui em um roteamento do cliente

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

  // coloca-se na sessão o nome da página para a qual se está indo — sem redirecionamento do servidor
  context.store.commit('replace', { serverRedirection: false, from: context.route.name })
  // salva-se o store na sessão [nuxt]
  const session = context.app.$session()
  session.value.store = context.store.state
  session.save(context)
}
  • as linhas 19 a 24 são adicionadas;
  • linha 20: insere-se no store o nome da página [context.route.name] que será exibida e que, portanto, no próximo roteamento, será a página de onde viemos. Além disso, veremos que, no roteamento do servidor [nuxt], este precisa saber se o roteamento atual é resultado de um redirecionamento anterior do servidor [nuxt]. Aqui, esse não é o caso e, portanto, definimos a propriedade [serverRedirection] como [false];
  • linhas 22-24: o estado do store é armazenado na sessão [nuxt] (linha 23) e, em seguida, a sessão [nuxt] é salva em um cookie (linha 24), que, por sua vez, será armazenado no navegador do cliente [nuxt];

Voltemos ao script de roteamento do servidor [nuxt]:


/* eslint-disable no-console */
export default function(context) {
  // quem executa esse 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'
  // Para onde estamos indo?
  const to = context.route.name
  // possível redirecionamento
  let redirection = ''
  // gerenciamento do roteamento concluído
  let done = false

  // já estamos em um redirecionamento do servidor [nuxt]?
  if (store.state.serverRedirection) {
    // 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
  }
  
  // controle 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 usuário 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 usuário 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) {
    // registramos o redirecionamento no store
    store.commit('replace', { serverRedirection: true })
  } else {
    // sem redirecionamento
    store.commit('replace', { serverRedirection: false, from: to })
  }
  // estamos salvando o store na sessão [nuxt]
  const session = context.app.$session()
  session.value.store = store.state
  session.save(context)
  // é feita a eventual redirecionamento
  if (redirection) {
    context.redirect({ name: redirection })
  }
}
  • linhas 6-9: recupera-se o valor de [from] no armazenamento do servidor [nuxt];
  • linha 11: registra-se o destino do roteamento atual;
  • linha 13: o roteamento pode levar a um redirecionamento do navegador do cliente. [redirection] será o destino desse redirecionamento;
  • linha 15: [done] para [true] indica que o roteamento foi concluído;
  • linhas 17-21: verifica-se primeiro se o roteamento atual decorre de uma solicitação de redirecionamento enviada ao navegador do cliente. Essa informação está armazenada na propriedade [serverRedirection] do store. Se essa propriedade estiver definida como verdadeiro, isso significa que o servidor [nuxt] enviou um redirecionamento ao navegador do cliente durante a solicitação anterior ao servidor [nuxt]. Nesse caso, não há roteamento a ser feito. Na solicitação anterior, o roteador do servidor [nuxt] decidiu que o navegador do cliente deveria ser redirecionado. Essa decisão não precisa ser questionada por um novo roteamento;
  • linhas 23-27: verifica-se se o roteamento em andamento é uma recarga da página. Se for, deixa-se que ocorra;
  • 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 roteamento não é a página [index]. Nesse 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 usuário não está autenticado e o destino do roteamento atual não é a página [authentification]. Nesse caso, o roteamento é recusado e permanecemos onde estávamos;
  • linhas 48-54: tratamos o caso em que a sessão jSON foi iniciada, o usuário está autenticado e o destino do roteamento atual não é nem a página [get-admindata], nem a página [fin-session], que são, então, os únicos destinos possíveis. Nesse caso, o roteamento solicitado é recusado e retorna-se ao ponto anterior;
  • linhas 56-62: trata-se do caso em que [adminData] foi obtido. Nesse caso, há apenas um destino possível para o roteamento: a página [fin-session]. Se não fosse essa a página solicitada, o roteamento é recusado e voltamos ao ponto anterior;
  • linhas 64-72: se houve redirecionamento, registra-se no armazenamento do servidor [nuxt]: [serverRedirection: true]. Observe-se que não se atribui nenhum valor à propriedade [from] do armazenamento. A razão para isso é que haverá redirecionamento do navegador do cliente e vimos que, nesse caso, não há roteamento (linhas 17-20) e a propriedade [from] do store não é utilizada;
  • linhas 66-69: se não houver redirecionamento, isso também é registrado no armazenamento do servidor [nuxt]: [serverRedirection: false]. Além disso, o roteamento em andamento 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 se grava [from: to];
  • linhas 73-76: salvamos o store na sessão [nuxt], que por sua vez é salva em um cookie;
  • linhas 77-80: se [redirection] não estiver vazio, solicita-se ao navegador que se redirecione. Caso contrário (o que não é mostrado aqui), o ciclo de vida do servidor [nuxt] continuará: a página [to] será processada pelo servidor [nuxt] e enviada ao navegador do cliente [nuxt] com o cookie de sessão [nuxt];

O roteamento escolhido aqui para o servidor [nuxt] é arbitrário. Poderíamos ter escolhido outro ou, como já foi dito, não ter feito nenhum. O escolhido acima tem a vantagem de sempre manter o aplicativo em um estado estável, independentemente da página URL solicitada pelo usuário.

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

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

Em ambos os casos, a página original será executada novamente com sua chamada assíncrona ao servidor de cálculo do imposto. Vejamos um exemplo. Se, após a autenticação, o usuário recarregar a página (F5). Nesse caso, no roteamento acima, temos: [to]=[from]=[authentification]. Não há redirecionamento. A página [to=authentification] será executada pelo servidor [nuxt]. Se nada for feito, a função [asyncData] será executada novamente. Isso é desnecessário, já que a autenticação já foi realizada.

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


// dados assíncronos
  async asyncData(context) {
    // registro
    console.log('[authentification asyncData started]')
    // não se repete a operação 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 for constatado no store que a autenticação já foi feita, então retorna-se diretamente o resultado desejado (linha 8);

Fazemos o mesmo para todas as páginas:

Página [index]:


// dados assíncronos
  async asyncData(context) {
    // registro
    console.log('[index asyncData started]')
    // não se faz a mesma coisa duas vezes 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) {
    // log
    console.log('[get-admindata asyncData started]')
    // não se faz a mesma coisa 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 erro
      context.app.$eventBus().$emit('errorLoading', false)
    }
    try {
   ...  

Página [fin-session]


// dados assíncronos
  async asyncData(context) {
    // registro
    console.log('[fin-session asyncData started]')
    // não se faz as coisas duas vezes 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, excluir o cookie de sessão [nuxt] e o cookie PHP do navegador que está executando o cliente [nuxt], a fim de iniciar a partir de uma situação limpa. Abaixo, um exemplo com o navegador Chrome:

Image

16.5. Conclusion

O roteamento do servidor [nuxt] é complexo, pois é necessário prever todos os URL que o usuário possa digitar manualmente. Trata-se de um caso clássico. Um aplicativo [nuxt] não se destina a ser usado dessa forma. Uma vez que a página [index] seja servida pelo roteador do servidor [nuxt], seria possível redirecionar as chamadas subsequentes feitas ao servidor para uma página de erro.

No caso específico do nosso exemplo [nuxt-13], o roteamento do servidor [nuxt] era desnecessário. O roteamento padrão (na verdade, a ausência de roteamento) no exemplo [nuxt-12] era perfeitamente adequado.