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]:

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:

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.