15. Exemplo [nuxt-12]: solicitações HTTP com axios
15.1. Présentation
Neste novo exemplo, vamos descobrir como, nas funções [asyncData], é possível efetuar pedidos HTTP com a biblioteca [axios]. Além disso, vamos utilizar conceitos já adquiridos:
- a utilização de plugins do exemplo [nuxt-06]:
- a persistência do store num cookie de sessão do exemplo [nuxt-06];
- o controlo da navegação com middlewares do exemplo [nuxt-09];
- a gestão de erros do exemplo [nuxt-11];
A arquitetura do exemplo será a seguinte:

- a aplicação [nuxt] será armazenada no servidor [node.js] [3], descarregada pelo navegador [1], que a executará em seguida;
- tanto o cliente [nuxt] [1] como o servidor [nuxt] [3] enviarão pedidos HTTP ao servidor de dados [2]. Este servidor será o servidor de cálculo de impostos desenvolvido na secção PHP 7. Utilizaremos a sua versão mais recente, a versão 14, com as solicitações CORS autorizadas;
A arquitetura do exemplo pode ser simplificada da seguinte forma:

- em [1], o servidor [node.js] fornece as páginas [nuxt] ao navegador [2]. É a camada [web] [8] do servidor que fornece estas páginas. Para entregar a página, o servidor pode ter solicitado dados externos ao servidor de dados [3]. É a camada [DAO] [9] que efetua as solicitações HTTP necessárias;
- a cada chamada de página ao servidor [node.js][1], o navegador [2] recebe a aplicação completa [nuxt], que passa então a ser executada no modo SPA. O bloco [UI] (Interface do Utilizador) [4] apresenta páginas [vue.js] ao utilizador. As ações do utilizador ou o ciclo de vida natural das páginas podem provocar chamadas de dados externos ao servidor de dados [3]. É a camada [DAO] [5] que, então, efetua as consultas HTTP necessárias;
15.2. Estrutura do projeto

15.3. O ficheiro de configuração [nuxt.config.js]
O projeto será controlado pelo ficheiro [nuxt.config.js] seguinte:
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Introduction à [nuxt.js]',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'ssr routing loading asyncdata middleware plugins store'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},
/*
** Customize the progress-bar color
*/
loading: false,
/*
** Global CSS
*/
css: [],
/*
** Plugins to load before mounting the App
*/
plugins: [
{ src: '@/plugins/client/plgSession', mode: 'client' },
{ src: '@/plugins/server/plgSession', mode: 'server' },
{ src: '@/plugins/client/plgDao', mode: 'client' },
{ src: '@/plugins/server/plgDao', mode: 'server' },
{ src: '@/plugins/client/plgEventBus', mode: 'client' }
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Documentação: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
// https://www.npmjs.com/package/cookie-universal-nuxt
'cookie-universal-nuxt'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) { }
},
// diretório do código-fonte
srcDir: 'nuxt-12',
// router
router: {
// raiz dos URL da aplicação
base: '/nuxt-12/',
// middleware de encaminhamento
middleware: ['routing']
},
// servidor
server: {
// porta de serviço, 3000 por predefinição
port: 81,
// endereços de rede em escuta, por predefinição localhost: 127.0.0.1
// 0.0.0.0 = todos os endereços de rede da máquina
host: 'localhost'
},
// ambiente
env: {
// configuração do Axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/versão-14',
// configuração do cookie de sessão [nuxt]
maxAge: 60 * 5
}
}
- linha 22: somos nós próprios que gerimos o alerta de espera pela conclusão de uma ação assíncrona;
- linha 31: iremos utilizar vários plugins que serão especializados quer para o cliente, quer para o servidor, mas não para ambos em simultâneo;
- linha 52: o módulo [axios] está integrado no [nuxt]. Isto terá como consequência que o objeto [axios], que efetuará as solicitações HTTP daaplicação [nuxt] para o servidor PHP de cálculo do imposto, estará disponível no [context.$axios];
- linha 54: o módulo [cookie-universal-nuxt] vai permitir-nos guardar a sessão [nuxt] num cookie;
- linha 60: a propriedade [axios] permite-nos configurar o módulo [@nuxtjs/axios] da linha 52. Não utilizaremos esta possibilidade, preferindo a propriedade [env] da linha 88;
- linha 90: tempo máximo de espera pela resposta do servidor de cálculo do imposto;
- linha 91: necessário para o cliente [nuxt] — autoriza a utilização de cookies nas comunicações com o servidor de cálculo do imposto;
- linha 92: o URL de base do servidor de cálculo de impostos;
- linha 94: duração da sessão Nuxt (5 min);
- linha 77: a navegação entre o cliente e o servidor [nuxt] será controlada por um middleware de encaminhamento;
15.4. A camada [UI] da aplicação

Vamos conceder à aplicação [nuxt] acesso ao API do servidor de cálculo de impostos através da seguinte vista:

- no [2], o menu que dá acesso ao API do servidor de cálculo de impostos:
- [Authentification]: corresponde à página [authentification]. Esta página efetua um pedido de autenticação junto do servidor de cálculo de impostos com os identificadores [admin, admin], que são, de momento, os únicos autorizados. O resultado apresentado é semelhante ao da página [3];
- [Requête AdminData]: corresponde à página [get-admindata]. Esta página solicita ao servidor de cálculo de impostos os dados, aqui designados por [adminData], que permitem o cálculo do imposto. O resultado apresentado é semelhante ao de [3];
- [Fin session impôt]: corresponde à página [fin-session]. Esta página envia um pedido de fim de sessão PHP ao servidor de cálculo do imposto. O servidor cancela então a sessão PHP atual e inicia uma nova sessão em branco;
15.5. As camadas [dao] da aplicação [nuxt]
Conforme indicado acima, a arquitetura da aplicação [nuxt] será a seguinte:

- em [1], o servidor [node.js] fornece as páginas [nuxt] ao navegador [2]. É a camada [web] [8] do servidor que fornece estas páginas. Para entregar a página, o servidor pode ter solicitado dados externos ao servidor de dados [3]. É a camada [DAO] [9] que efetua as solicitações HTTP necessárias;
- a cada chamada de página ao servidor [node.js][1], o navegador [2] recebe a aplicação completa [nuxt], que passa então a ser executada no modo SPA. O bloco [UI] (Interface do Utilizador) [4] apresenta páginas [vue.js] ao utilizador. As ações deste bloco ou o ciclo de vida das páginas podem provocar chamadas de dados externos ao servidor de dados [3]. É a camada [DAO] [5] que, então, efetua as consultas HTTP necessárias;
Utilizaremos a versão 14 do servidor de cálculo de impostos desenvolvido no documento |Introdução à linguagem PHP7 através de um exemplo|. Utilizaremos apenas uma parte da sua API (Interface de Programação de Aplicações) jSON:
Consulta | Resposta |
| |
| |
| |
| |
15.5.1. A camada [dao] do servidor [nuxt]

O servidor [node.js] [1] irá utilizar a camada [dao] descrita no documento |Introdução ao framework VUE.JS através de um exemplo|. Recordamos aqui o seu código:
'use strict';
// importações
import qs from 'qs'
class Dao {
// construtor
constructor(axios) {
this.axios = axios;
// cookie de sessão
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// inicialização da sessão
async initSession() {
// opções da solicitação HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// parâmetros da URL
params: {
action: 'init-session',
type: 'json'
}
};
// execução da consulta HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// opções da consulta HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// corpo do POST
data: qs.stringify({
user: user,
password: password
}),
// parâmetros do URL
params: {
action: 'authentifier-utilisateur'
}
};
// execução da consulta HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// opções da consulta HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// parâmetros da consulta URL
params: {
action: 'get-admindata'
}
};
// execução da consulta HTTP
const data = await this.getRemoteData(options);
// resultado
return data;
}
async getRemoteData(options) {
// para o cookie de sessão
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execução da consulta HTTP
let response;
try {
// solicitação assíncrona
response = await this.axios.request('main.php', options);
} catch (error) {
// o parâmetro [error] é uma instância de exceção — pode assumir várias formas
if (error.response) {
// a resposta do servidor está em [error.response]
response = error.response;
} else {
// o erro é reenviado
throw error;
}
}
// a resposta é o conjunto completo da resposta HTTP do servidor (cabeçalhos HTTP + a própria resposta)
// recupera-se o cookie de sessão, caso exista
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie é um array
// procura-se o cookie de sessão neste array
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// procura-se o cookie de sessão
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// guarda-se o cookie de sessão
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// encontrámos
trouvé = true;
} else {
// elemento seguinte
i++;
}
}
}
// a resposta do servidor está em [response.data]
return response.data;
}
}
// exportação da classe
export default Dao;
- todos os métodos da camada [dao] devolvem o objeto enviado pelo servidor de dados [{action : ‘xx’, état : nn, réponse : {...}] com:
- [action]: o nome da ação executada pelo servidor de dados;
- [état]: indicador numérico:
- [initSession]: estado=700 para uma resposta sem erros;
- [authentifierUtilisateur]: estado=200 para uma resposta sem erros;
- [getAdminData]: estado=1000 para uma resposta sem erros;
- [fin-session]: estado=400 para uma resposta sem erros;
- [réponse]: resposta associada ao indicador numérico [état]. Pode variar consoante esse indicador numérico;
Analisemos o construtor da classe [Dao]:
// construtor
constructor(axios) {
this.axios = axios;
// cookie de sessão
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- linha 2: o objeto [axios] fornecido como argumento ao construtor é fornecido pelo código chamador. É ele que irá efetuar as solicitações HTTP;
- linha 5: o nome do cookie de sessão enviado pelo servidor de dados, escrito como PHP;
- linha 6: o cookie de sessão que é trocado entre a camada [dao] e o servidor de dados. Este é inicializado pela função [getRemoteData] nas linhas 67-113;
No que diz respeito ao cookie de sessão, devemos considerar duas camadas [dao] distintas:
- a do navegador;
- a do servidor;
Teremos de gerir três cookies de sessão:
- o que é trocado entre o cliente [nuxt] e o servidor PHP 7;
- o que é trocado entre o servidor [nuxt] e o servidor PHP 7;
- o cookie trocado entre o cliente [nuxt] e o servidor [nuxt];
Vamos garantir que o cookie de sessão com o servidor PHP seja o mesmo para o cliente e para o servidor [nuxt]. Chamaremos a este cookie «cookie de sessão PHP». Este cookie é o dos casos 1 e 2. Chamaremos «cookie de sessão [nuxt]» ao cookie do caso 3. Teremos, portanto, duas sessões:
- uma sessão PHP com o cookie de sessão PHP;
- uma sessão [nuxt] com o cookie de sessão [nuxt];
Por que razão utilizar o mesmo cookie para as sessões PHP do cliente e do navegador [nuxt]? Queremos que a aplicação possa comunicar com o servidor PHP, independentemente de se tratar do cliente ou do servidor [nuxt]:
- se uma ação A do servidor [nuxt] colocar o servidor PHP num estado E, esse estado é refletido na sessão PHP mantida pelo servidor PHP;
- utilizando o mesmo cookie de sessão PHP que o servidor, uma ação B do cliente [nuxt] que se seguisse à ação A do servidor [nuxt] encontraria o servidor PHP noestado E deixado pelo servidor [nuxt] e poderia, assim, basear-se no trabalho já realizado pelo servidor [nuxt];
- se, após a ação B do cliente [nuxt], se seguir uma ação C do servidor [nuxt], pela mesma razão que anteriormente, essa ação poderá basear-se no trabalho realizado pela ação B do cliente [nuxt];
Para que o navegador do cliente [nuxt] possa comunicar com o servidor PHP de cálculo de impostos, utilizaremos a versão 14 deste servidor, que permite chamadas entre domínios, ou seja, as chamadas de um navegador para o servidor PHP. As chamadas do servidor [nuxt] para o servidor PHP, por sua vez, não são chamadas entre domínios. Este conceito existe apenas para as chamadas efetuadas a partir de um navegador.
Voltemos ao código do construtor da classe [Dao] anterior:
// construtor
constructor(axios) {
this.axios = axios;
// cookie de sessão
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- as linhas 5 e 6 correspondem ao cookie da sessão PHP com o servidor de cálculo do imposto;
A gestão do cookie de sessão PHP acima referida não é adequada para o servidor [nuxt]: a sua camada [dao] é instanciada a cada nova solicitação feita ao servidor [nuxt]. Recorde-se, de facto, que solicitar uma página ao servidor [nuxt] equivale a reiniciar a aplicação [nuxt]. Assim, quando, no final da primeira solicitação feita ao servidor de dados pelo servidor [nuxt], o cookie de sessão PHP da camada [dao] é inicializado, esse valor é perdido na solicitação seguinte HTTP do mesmo servidor [nuxt], uma vez que, entretanto, a sua camada [dao] foi recriada, o construtor foi reexecutado e o cookie de sessão PHP foi reinicializado com a cadeia vazia (linha 6);
Uma solução consiste em utilizar outro construtor para a camada [dao] do servidor:
// construtor
constructor(axios, phpSessionCookie) {
// biblioteca axios
this.axios = axios
// valor do cookie de sessão
this.phpSessionCookie = phpSessionCookie
// nome do cookie de sessão do servidor PHP
this.phpSessionCookieName = 'PHPSESSID'
}
- linha 2: desta vez, o cookie de sessão PHP será fornecido ao construtor da camada [dao] do servidor de dados;
Como é que o servidor [nuxt] poderá fornecer este cookie de sessão PHP ao construtor da sua camada [dao]? Iremos armazenar o cookie de sessão PHP no cookie de sessão [nuxt] trocado entre o navegador e o servidor [nuxt]. O processo é o seguinte:
- a aplicação [nuxt] é iniciada;
- quando o servidor [nuxt] efetua a sua primeira solicitação HTTP ao servidor PHP, armazena o cookie de sessão PHP que recebeu no cookie de sessão [nuxt] que troca com o cliente [nuxt];
- o navegador que hospeda o cliente [nuxt] recebe este cookie de sessão [nuxt] e, por conseguinte, reenvia-o sistematicamente em cada nova solicitação ao servidor [nuxt];
- quando o servidor [nuxt] tiver de efetuar uma nova solicitação ao servidor PHP, irá encontrar o cookie de sessão PHP no cookie de sessão [nuxt] que o navegador lhe tiver enviado. Enviá-lo-á então ao servidor PHP;
Existem, de facto, dois cookies de sessão e não se devem confundir:
- o cookie de sessão [nuxt] trocado entre o servidor [nuxt] e o navegador do cliente [nuxt];
- o cookie de sessão PHP, trocado entre o servidor [nuxt] e o servidor PHP ou entre o cliente [nuxt] e o servidor PHP;
Voltemos agora ao código do método da classe [Dao]. Este não inclui uma função para encerrar a sessão PHP com o servidor de cálculo de impostos. Adicionamos essa função:
// fim da sessão de cálculo do imposto
async finSession() {
// opções da solicitação HHTP [get /main.php?action=fin-session]
const options = {
method: 'GET',
// parâmetros da consulta URL
params: {
action: 'fin-session'
}
}
// execução da consulta HTTP
const data = await this.getRemoteData(options)
// resultado
return data
}
Nos testes, descobrimos que a função [getRemoteData], chamada na linha 12, não é adequada para o método [finSession]:
async getRemoteData(options) {
// para o cookie de sessão
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execução da consulta HTTP
let response;
try {
// solicitação assíncrona
response = await this.axios.request('main.php', options);
} catch (error) {
// o parâmetro [error] é uma instância de exceção — pode assumir várias formas
if (error.response) {
// a resposta do servidor está em [error.response]
response = error.response;
} else {
// o erro é reenviado
throw error;
}
}
// a resposta é o conjunto completo da resposta HTTP do servidor (cabeçalhos HTTP + a própria resposta)
// recupera-se o cookie de sessão, caso exista
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie é um array
// procura-se o cookie de sessão neste array
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// procura-se o cookie de sessão
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// guarda-se o cookie de sessão
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// encontrado
trouvé = true;
} else {
// elemento seguinte
i++;
}
}
}
// a resposta do servidor está em [response.data]
return response.data;
}
- linhas 30-43: procura-se o cookie [PHPSESSID=xxx]. Se for encontrado, é armazenado na classe (linha 36);
Este código não é compatível com o novo método [finSession], pois na ação [fin-session], o servidor PHP envia dois cookies com o nome [PHPSESSID]. Eis um exemplo obtido com um cliente [Postman]:

- em [1], o pedido do cliente [Postman];
- em [3], a resposta do servidor PHP;
- em [4], os cabeçalhos HTTP da resposta do servidor PHP;

- em [5], o servidor PHP indica, em primeiro lugar, que eliminou a sessão PHP atual;
- no [6], o servidor PHP envia o cookie da nova sessão PHP;
Com o código atual, a função [getRemoteData] recupera o cookie [5], quando o que deve ser memorizado é o cookie [6].
É, portanto, necessário alterar o código da função [getRemoteData]:
async getRemoteData(options) {
// existe um cookie de sessão PHP?
if (this.phpSessionCookie) {
// existem cabeçalhos?
if (!options.headers) {
// cria-se um objeto vazio
options.headers = {}
}
// cabeçalho do cookie de sessão PHP
options.headers.Cookie = this.phpSessionCookie
}
// execução da solicitação HTTP
let response
try {
// pedido assíncrono
response = await this.axios.request('main.php', options)
} catch (error) {
// o parâmetro [error] é uma instância de exceção — pode assumir várias formas
if (error.response) {
// a resposta do servidor está em [error.response]
response = error.response
} else {
// o erro é reenviado
throw error
}
}
// a resposta é o conjunto completo da resposta HTTP do servidor (cabeçalhos HTTP + a própria resposta)
// procura-se o cookie de sessão PHP entre os cookies recebidos
// todos os cookies recebidos
const cookies = response.headers['set-cookie']
if (cookies) {
// os cookies constituem uma tabela
// procura-se o cookie de sessão PHP neste array
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// procura-se o cookie de sessão PHP
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// guarda-se o cookie de sessão PHP
const phpSessionCookie = results[1]
// existe a palavra [deleted] nesta tabela?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// temos o cookie de sessão correto PHP
this.phpSessionCookie = phpSessionCookie
// foi encontrado
trouvé = true
} else {
// elemento seguinte
i++
}
} else {
// elemento seguinte
i++
}
}
}
// a resposta do servidor está em [response.data]
return response.data
}
- linha 41: foi encontrado um cookie com o nome [PHPSESSID]. Este é memorizado localmente;
- linha 43: verifica-se se, no cookie guardado, existe a cadeia [PHPSESSID=deleted];
- linha 46: se a resposta for «não», significa que foi encontrado o cookie correto [PHPSESSID]. Armazenamo-lo na classe;
Após a função [getRemoteData], o cookie de sessão PHP é armazenado na classe, em [this.phpSessionCookie]. Disse-se que a classe era instanciada a cada nova solicitação HTTP do servidor [nuxt]. O cookie de sessão PHP deve, portanto, ser extraído da classe. Para tal, adiciona-se um novo método à mesma:
// acesso ao cookie da sessão PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
- o servidor [nuxt] solicita uma ação à sua camada [dao], fornecendo o cookie de sessão PHP ao seu construtor, caso este exista;
- uma vez realizada a ação, o servidor [nuxt] recupera o cookie de sessão PHP memorizado pela camada [dao] utilizando o método [getPhpSessionCookie] anterior. Este cookie pode ser o mesmo que o anterior ou outro. Este último caso ocorre em duas ocasiões:
- durante a execução do método [initSession] (não existia anteriormente nenhum cookie de sessão PHP);
- durante a execução do método [finSession] (o servidor PHP altera o cookie de sessão PHP);
É de salientar uma particularidade relativa ao cookie de sessão PHP. O servidor [nuxt] nem sempre recebe este cookie do servidor PHP. Com efeito, este último apenas o envia uma vez. Depois disso, deixa de o enviar. Ao analisar o código do [getRemoteData] e o do [getPhpSessionCookie], verifica-se que, quando o servidor PHP não envia nenhum cookie de sessão, a função [getPhpSessionCookie] devolve então o cookie de sessão PHP fornecido ao construtor. É assim que o servidor envia sempre ao servidor PHP o último cookie de sessão PHP que este lhe enviou.
15.5.2. A camada [dao] do cliente [nuxt]

Para o cliente [nuxt], que é executado num navegador, retomamos o código da classe [Dao] do documento |Introdução ao framework VUE.JS através de um exemplo|:
"use strict";
// importações
import qs from "qs";
class Dao {
// construtor
constructor(axios) {
this.axios = axios;
}
// inicialização da sessão
async initSession() {
// opções da solicitação HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// parâmetros da URL
params: {
action: "init-session",
type: "json"
}
};
// execução da consulta HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// opções da consulta HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
// corpo do POST
data: qs.stringify({
user: user,
password: password
}),
// parâmetros do URL
params: {
action: "authentifier-utilisateur"
}
};
// execução da consulta HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// opções da consulta HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// parâmetros da consulta URL
params: {
action: "get-admindata"
}
};
// execução da consulta HTTP
const data = await this.getRemoteData(options);
// resultado
return data;
}
async getRemoteData(options) {
// execução da consulta HTTP
let response;
try {
// consulta assíncrona
response = await this.axios.request("main.php", options);
} catch (error) {
// o parâmetro [error] é uma instância de exceção — pode assumir várias formas
if (error.response) {
// a resposta do servidor está em [error.response]
response = error.response;
} else {
// o erro é reenviado
throw error;
}
}
// a resposta é o conjunto completo da resposta HTTP do servidor (cabeçalhos HTTP + a própria resposta)
// a resposta do servidor está em [response.data]
return response.data;
}
}
// exportação da classe
export default Dao;
Este código distingue-se da camada [dao] do servidor [nuxt] pelo facto de não gerir o cookie de sessão PHP com o servidor de cálculo de impostos: é o navegador que o faz.
Tal como fizemos para a camada [dao] do servidor [nuxt], vamos adicionar um método [finSession]:
// fim da sessão de cálculo do imposto
async finSession() {
// opções da consulta HHTP [get /main.php?action=fin-session]
const options = {
method: 'GET',
// parâmetros do URL
params: {
action: 'fin-session'
}
}
// execução da consulta HTTP
const data = await this.getRemoteData(options)
// resultado
return data
}
Quando o cliente [nuxt] executa este método, recebe, tal como o servidor [nuxt], dois cookies de sessão PHP. Na verdade, é o navegador que os recebe e gere corretamente a situação: mantém apenas o cookie da nova sessão PHP, iniciada pelo servidor de cálculo do imposto. Assim, na próxima ação do cliente [nuxt] dirigida ao servidor PHP, o cookie de sessão PHP estará correto, uma vez que é o navegador que o envia. No entanto, há um problema: o servidor [nuxt] não tem conhecimento de que o cookie de sessão PHP foi alterado. Nas suas comunicações com o servidor PHP, irá então enviar um cookie de sessão PHP que já não existe, o que irá causar problemas. O cliente [nuxt] deveria avisar o servidor [nuxt] de que o cookie de sessão PHP mudou e transmitir-lhe esse cookie. Sabemos como ele pode fazer isso: através do cookie de sessão [nuxt], o cookie trocado entre o cliente e o servidor [nuxt]. O cliente [nuxt] tem, pelo menos, duas formas de recuperar o novo cookie de sessão PHP:
- solicitando-o ao navegador;
- utilizando o método [getRemoteData] do servidor, que sabe como recuperar o novo cookie de sessão PHP;
Vamos utilizar a segunda solução, uma vez que já está pronta a utilizar. O método [getRemoteData] do cliente [nuxt] passa então a ser o seguinte:
async getRemoteData(options) {
// execução da consulta HTTP
let response
try {
// consulta assíncrona
response = await this.axios.request('main.php', options)
} catch (error) {
// o parâmetro [error] é uma instância de exceção — pode assumir várias formas
if (error.response) {
// a resposta do servidor está em [error.response]
response = error.response
} else {
// o erro é reenviado
throw error
}
}
// a resposta é o conjunto completo da resposta HTTP do servidor (cabeçalhos HTTP + a própria resposta)
// procura-se o cookie de sessão PHP entre os cookies recebidos
// todos os cookies recebidos
const cookies = response.headers['set-cookie']
if (cookies) {
// os cookies constituem uma tabela
// procura-se o cookie de sessão PHP neste array
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// procura-se o cookie de sessão PHP
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// guarda-se o cookie de sessão PHP
const phpSessionCookie = results[1]
// existe a palavra [deleted] nesta tabela?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// temos o cookie de sessão correto PHP
this.phpSessionCookie = phpSessionCookie
// encontrámos
trouvé = true
} else {
// o elemento seguinte
i++
}
} else {
// elemento seguinte
i++
}
}
}
// a resposta do servidor está em [response.data]
return response.data
}
Em [getRemoteData], mantivemos apenas o código que processa a resposta do servidor PHP à pesquisa do cookie de sessão PHP. Não foi mantido o código que incluía o cookie de sessão PHP na solicitação ao servidor PHP, uma vez que é o navegador que hospeda o cliente [nuxt] que se encarrega disso.
Assim que o cookie de sessão PHP for obtido pelo cliente [nuxt], este deve ser inserido na sessão [nuxt] para que o servidor [nuxt] possa beneficiar dele. Não é a camada [dao] que se encarrega disso, mas ela fornece acesso, através de um método, ao cookie de sessão PHP que memorizou:
// acesso ao cookie da sessão PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
A função [getPhpSessionCookie] nem sempre devolve um cookie de sessão válido:
- é importante lembrar que a camada [dao] do cliente [nuxt] é persistente. É instanciada uma vez e permanece depois na memória;
- enquanto o servidor PHP não enviar um cookie de sessão PHP ao cliente [nuxt], a função [getPhpSessionCookie] do cliente [nuxt] devolve o valor [undefined];
- quando o servidor PHP envia um cookie de sessão PHP ao cliente [nuxt], este é armazenado em [this.phpSessionCookie] e permanecerá lá até ser substituído por um novo cookie de sessão PHP enviado pelo servidor PHP. A função [getPhpSessionCookie] do cliente [nuxt] devolve então o último cookie de sessão PHP recebido;
A camada [dao] do cliente [nuxt] difere da do servidor [nuxt] apenas num aspeto: ela própria não envia o cookie de sessão PHP, pois é o navegador que o faz. No entanto, optou-se por manter duas camadas [dao] distintas, uma vez que os raciocínios que conduzem às respetivas implementações são diferentes.
15.6. A sessão [nuxt]
![]()
A sessão [nuxt] (entre o cliente e o servidor Nuxt) será encapsulada no seguinte objeto [session]:
/* eslint-disable no-console */
// definição da sessão
const session = {
// conteúdo da sessão
value: {
// store não inicializado
initStoreDone: false,
// valor do store Vuex
store: ''
},
// armazenamento da sessão num cookie
save(context) {
// armazenamento do store na sessão
this.value.store = context.store.state
console.log('nuxt-session save=', this.value)
// armazenamento do valor da sessão
context.app.$cookies.set('nuxt-session', this.value, { path: context.base, maxAge: context.env.maxAge })
},
// reinicialização da sessão
reset(context) {
console.log('nuxt-session reset')
// reinicialização do valor armazenado
context.store.commit('reset')
// gravação do novo store na sessão e gravação da sessão
this.save(context)
}
}
// exportação da sessão
export default session
- linhas 5-10: a sessão tem apenas uma propriedade [value] com duas subpropriedades:
- [initStoreDone], que indica se o store foi inicializado ou não;
- [store]: o valor [store.state] do armazenamento Vuex da aplicação;
- linhas 12-18: o método [save] serve para guardar a sessão [nuxt] num cookie. Aqui, utiliza-se a biblioteca [cookie-universal-nuxt] para gerir o cookie. De notar o nome do cookie da sessão [nuxt]: [nuxt-session] (linha 17);
- linhas 20-26: o método [reset] reinicializa a sessão [nuxt];
- linha 23: o store Vuex é reinicializado e, em seguida, guardado na sessão, linha 25;
15.7. Os plugins de gestão da sessão [nuxt]

15.7.1. O plugin de gestão da sessão [nuxt] do serveur [nuxt]
Ao iniciar a aplicação, o servidor [nuxt] é o primeiro a entrar em funcionamento. Por conseguinte, é ele que irá inicializar a sessão [nuxt]. O script [server/plgSession] é o seguinte:
/* eslint-disable no-console */
// importação da sessão
import session from '@/entities/session'
export default (context, inject) => {
// gestão da sessão do servidor
console.log('[plugin server plgSession]')
// existe alguma sessão?
const value = context.app.$cookies.get('nuxt-session')
if (!value) {
// nova sessão
console.log("[plugin server plgSession], démarrage d'une nouvelle session")
} else {
// sessão existente
console.log("[plugin server plgSession], reprise d'une session existante")
session.value = value
}
// injetamos uma função em [context, Vue] que tornará a sessão atual
inject('session', () => session)
}
- linha 4: importa-se o código da sessão [nuxt];
- linha 11: recupera-se o valor do cookie da sessão [nuxt];
- linhas 12-15: se o cookie da sessão [nuxt] não existisse, então a sessão [nuxt] importada na linha 4 seria suficiente. Não há mais nada a fazer;
- linhas 15-19: se o cookie da sessão [nuxt] existisse, então, na linha 18, o seu valor seria armazenado na sessão importada na linha 4;
- linha 22: a sessão foi inicializada ou restaurada. Torna-se disponível através da função [$session];
15.7.2. O plugin de gestão da sessão [nuxt] do cliente [nuxt]
O script [client/plgSession] é o seguinte:
/* eslint-disable no-console */
// importação da sessão
import session from '@/entities/session'
export default (context, inject) => {
// gestão da sessão do cliente
console.log('[plugin client plgSession], reprise de la session [nuxt] du serveur')
// recuperamos a sessão existente do servidor Nuxt
session.value = context.app.$cookies.get('nuxt-session')
// injetamos uma função em [context, Vue] que tornará a sessão atual
inject('session', () => session)
}
- linha 4: a sessão [nuxt] é importada;
- linha 10: recupera-se a sessão [nuxt] atual no cookie [nuxt-session];
- linha 13: a sessão [nuxt], importada na linha 4, é devolvida através da função injetada [$session];
15.8. Os plugins das camadas [dao]

15.8.1. O plugin da camada [dao] do cliente [nuxt]
O script [client/plgDao] é o seguinte:
/* eslint-disable no-console */
// criamos um ponto de acesso à camada [Dao]
import Dao from '@/api/client/Dao'
export default (context, inject) => {
// configuração do axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
context.$axios.defaults.withCredentials = context.env.withCredentials
// instanciação da camada [dao]
const dao = new Dao(context.$axios)
// injeção de uma função [$dao] no contexto
inject('dao', () => dao)
// registo
console.log('[fonction client $dao créée]')
}
- linha 3: a camada [dao] do cliente [nuxt] é importada;
- linhas 6-8: configura-se oobjeto [context.$axios], que irá efetuar as consultas HTTP da camada [dao] do cliente [nuxt] com as informações do ficheiro [nuxt.config]:
// ambiente
env: {
// configuração do Axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/versão-14',
// configuração do cookie de sessão [nuxt]
maxAge: 60 * 5
}
- linha 10: a camada [dao] do cliente [nuxt] é instanciada;
- linha 12: a função [$dao] é injetada no contexto e nas páginas do cliente. Esta função dá acesso à camada [dao] da linha 10;
Conclui-se, portanto, que para aceder à camada [dao] do cliente [nuxt] quando este estiver a ser executado, escrever-se-á:
- [context.app.$dao()], quando o contexto for conhecido;
- [this.$dao()] numa página [Vue.js];
15.8.2. O plugin da camada [dao] do serveur [nuxt]
O script [server/plgDao] é o seguinte:
/* eslint-disable no-console */
// criamos um ponto de acesso à camada [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// configuração do axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// recuperamos o cookie de sessão
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instanciação da camada [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// injeção de uma função [$dao] no contexto
inject('dao', () => dao)
// registo
console.log('[fonction server $dao créée]')
}
- linha 3: a camada [dao] do servidor [nuxt] é importada;
- linhas 6-7: configura-se oobjeto [context.$axios], que irá efetuar as consultas HTTP da camada [dao] do servidor [nuxt] com as informações do ficheiro [nuxt.config]:
// ambiente
env: {
// configuração do Axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// configuração do cookie de sessão [nuxt]
maxAge: 60 * 5
}
- linha 9: recupera-se o armazenamento da aplicação [nuxt];
- linha 10: se a loja existir, recupera-se o cookie da sessão PHP, pois é necessário para instanciar a camada [dao] do servidor [nuxt];
- linha 13: instanciamos a camada [dao] do servidor [nuxt];
- linha 15: a função [$dao] é injetada no contexto e nas páginas do servidor [nuxt]. Esta função dá acesso à camada [dao] da linha 13;
Resta, portanto, reter que, para aceder à camada [dao] do servidor [nuxt] quando este estiver a ser executado, escrever-se-á:
- [context.app.$dao()], quando o contexto for conhecido;
- [this.$dao()] numa página [Vue.js];
15.9. O store Vuex
![]()
O store [Vuex] irá armazenar todos os dados que devem ser partilhados pelos diferentes componentes da aplicação [pages, client, serveur], sem que esses dados sejam, por isso, reativos.
/* eslint-disable no-console */
// estado da persiana
export const state = () => ({
// sessão jSON iniciada
jsonSessionStarted: false,
// utilizador autenticado
userAuthenticated: false,
// cookie de sessão PHP
phpSessionCookie: '',
// adminData
adminData: ''
})
// alterações no armazenamento
export const mutations = {
// substituição do estado
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
},
// reinicialização do armazenamento
reset() {
this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
}
}
// ações do armazenamento
export const actions = {
nuxtServerInit(store, context) {
// quem executa este código?
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// iniciar sessão
initStore(store, context)
}
}
function initStore(store, context) {
// store é a sessão a ser inicializada
// recuperar a sessão
const session = context.app.$session()
// A sessão já foi inicializada?
if (!session.value.initStoreDone) {
// inicia-se um novo «store»
console.log("nuxtServerInit, initialisation d'une nouvelle session")
// coloca-se o armazenamento na sessão
session.value.store = store.state
// o armazenamento está agora inicializado
session.value.initStoreDone = true
} else {
console.log("nuxtServerInit, reprise d'un store existant")
// o armazenamento está a ser atualizado com o armazenamento da sessão
store.commit('replace', session.value.store)
}
// a sessão é guardada
session.save(context)
// registo
console.log('initStore terminé, store=', store.state)
}
Os dados armazenados no store são os seguintes:
- linha 6: [jsonSessionStarted] será definido como verdadeiro assim que a inicialização de uma sessão jSON com o servidor PHP for bem-sucedida, quer tenha sido efetuada pelo cliente ou pelo servidor [nuxt]. No final desta inicialização, o cookie de sessão com o servidor PHP terá sido recuperado e colocado na propriedade [phpSessionCookie], linha 10;
- linha 8: [userAuthenticated] será definido como verdadeiro assim que a autenticação junto do servidor PHP for bem-sucedida, quer tenha sido efetuada pelo cliente ou pelo servidor [nuxt];
- linha 12: [adminData] será o valor [adminData] obtido junto do servidor PHP após a autenticação bem-sucedida;
- linhas 18-22: a alteração [replace] permite inicializar as propriedades anteriores com as de um objeto passado como parâmetro;
- linhas 24-26: a mutação [reset] repõe os valores iniciais das propriedades do store;
- linhas 31-37: a função [nuxtServerInit] delega a sua tarefa à função [initStore];
- linhas 39-60: a função [initStore] tem duas funções:
- se o armazenamento não tiver sido inicializado, é inicializado e colocado na sessão;
- se o store já tiver sido inicializado, o seu valor é recuperado na sessão [nuxt];
- linha 42: recupera-se a sessão Nuxt;
- linha 44: verifica-se se o store foi inicializado:
- se não for o caso, coloca-se o valor inicial do store na sessão (linha 48);
- depois, na linha 50, indica-se que o store foi inicializado;
- linhas 51-55: se o store tiver sido inicializado, utiliza-se esse mesmo, na linha 54, para inicializar o store com o valor contido na sessão;
- linha 57: em todos os casos, a sessão é guardada no cookie [nuxt-session], juntamente com o store que contém;
15.10. O plugin [plgEventBus]

Este plugin tem como objetivo tornar um canal de eventos acessível ao cliente [nuxt] através de uma função [$eventBus] injetada no contexto do cliente [nuxt]. Não faz sentido injetá-la no contexto do servidor [nuxt], uma vez que este não sabe gerir eventos. No entanto, já verificámos que injetá-la no lado do servidor e, em seguida, utilizá-la não provoca qualquer erro.
/* eslint-disable no-console */
// cria-se um bus de eventos entre as vistas
import Vue from 'vue'
export default (context, inject) => {
// o canal de eventos
const eventBus = new Vue()
// injeção de uma função [$eventBus] no contexto
inject('eventBus', () => eventBus)
// registo
console.log('[fonction $eventBus créée]')
}
Já nos deparámos com este plugin no parágrafo «ligação». A função [$eventBus] estará disponível no cliente através das anotações:
- [context.app.$eventBus()], sempre que o contexto estiver disponível;
- [this.$eventBus()] nas páginas [Vue.js] do cliente;
15.11. Os componentes da aplicação [nuxt]

O componente [layout] é o dos exemplos anteriores:
<!-- disposição das vistas -->
<template>
<!-- linha -->
<div>
<b-row>
<!-- área com três colunas -->
<b-col v-if="left" cols="3">
<slot name="left" />
</b-col>
<!-- área com nove colunas -->
<b-col v-if="right" cols="9">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// parâmetros
props: {
left: {
type: Boolean
},
right: {
type: Boolean
}
}
}
</script>
O componente [navigation] é o seguinte:
<template>
<!-- menu Bootstrap com três opções -->
<b-nav vertical>
<b-nav-item to="/authentification" exact exact-active-class="active">
Authentification
</b-nav-item>
<b-nav-item to="/get-admindata" exact exact-active-class="active">
Requête AdminData
</b-nav-item>
<b-nav-item to="/fin-session" exact exact-active-class="active">
Fin session impôt
</b-nav-item>
</b-nav>
</template>
15.12. Os layouts da aplicação [nuxt]

15.12.1. [default]
O layout [default] é o utilizado para o exemplo [nuxt-11] no parágrafo «ligação»:
<template>
<div class="container">
<b-card>
<!-- uma mensagem -->
<b-alert show variant="success" align="center">
<h4>[nuxt-12] : requêtes HTTP avec axios</h4>
</b-alert>
<!-- a vista atual do encaminhamento -->
<nuxt />
<!-- mensagem de espera -->
<b-alert v-if="showLoading" show variant="light">
<strong>Requête au serveur de données en cours...</strong>
<div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
</b-alert>
<!-- erro numa operação assíncrona -->
<b-alert v-if="showErrorLoading" show variant="danger">
<strong>La requête au serveur de données a échoué : {{ errorLoadingMessage }}</strong>
</b-alert>
</b-card>
</div>
</template>
<script>
/* eslint-disable no-console */
export default {
name: 'App',
data() {
return {
showLoading: false,
showErrorLoading: false
}
},
// ciclo de vida
beforeCreate() {
console.log('[default beforeCreate]')
},
created() {
console.log('[default created]')
if (process.client) {
// a ouvir o evento [loading]
this.$eventBus().$on('loading', this.mShowLoading)
// bem como o evento [errorLoadingMessage]
this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
}
},
beforeMount() {
console.log('[default beforeMount]')
},
mounted() {
console.log('[default mounted]')
},
methods: {
// gestão da mensagem em espera
mShowLoading(value) {
console.log('[default mShowLoading], showLoading=', value)
this.showLoading = value
},
// erro numa operação assíncrona
mShowErrorLoading(value, errorLoadingMessage) {
console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
this.showErrorLoading = value
this.errorLoadingMessage = errorLoadingMessage
}
}
}
</script>
- linhas 10-14: apresentam a mensagem de espera pelo fim de uma operação assíncrona do cliente [nuxt];
- linhas 15-18: apresentam a eventual mensagem de erro de uma operação assíncrona;
- linha 37: a função [created] da página [default] é executada antes da função [mounted] das páginas;
- linha 39: se o executor for o cliente [nuxt], então a página [default] fica à espera dos eventos:
- [loading], que sinaliza o início ou o fim de uma espera. A função [mShowLoading] é então executada;
- [errorLoading], que indica que é necessário apresentar uma mensagem de erro. A função [mShowErrorLoading] é então executada;
- as páginas [nuxt]:
- exibem a mensagem de espera ao emitir o evento [‘loading’, true] no barramento de eventos;
- ocultam a mensagem de espera ao emitir o evento [‘loading’, false] no barramento de eventos;
- exibem uma mensagem de erro ao emitir o evento [‘errorLoading’, true] no barramento de eventos;
- ocultam a mensagem de erro ao emitir o evento [‘errorLoading’, false] no barramento de eventos;
15.12.2. [error]
O layout [error] apresenta uma mensagem de erro do sistema (não gerida pelo programador):
<!-- definição da vista HTML -->
<template>
<!-- formatação -->
<Layout :left="true" :right="true">
<!-- alerta na coluna da direita -->
<template slot="right">
<!-- mensagem sobre fundo rosa -->
<b-alert show variant="danger" align="center">
<h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
</b-alert>
</template>
<!-- menu de navegação na coluna da esquerda -->
<Navigation slot="left" />
</Layout>
</template>
<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */
import Layout from '@/components/layout'
import Navigation from '@/components/navigation'
export default {
name: 'Error',
// componentes utilizados
components: {
Layout,
Navigation
},
// propriedade [props]
props: { error: { type: Object, default: () => 'waiting ...' } },
// ciclo de vida
beforeCreate() {
// cliente e servidor
console.log('[error beforeCreate]')
},
created() {
// cliente e servidor
console.log('[error created, error=]', this.error)
},
beforeMount() {
// apenas cliente
console.log('[error beforeMount]')
},
mounted() {
// apenas cliente
console.log('[error mounted]')
}
}
</script>
15.13. A página [index] executada pelo servidor [nuxt]

A página [index.vue] tem a particularidade de ser acessível apenas através do servidor [nuxt]. Não é apresentado qualquer link ao utilizador para aceder à mesma através do cliente [nuxt]. O seu código é o seguinte:
<!-- página principal -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem-->
<b-alert slot="right" show variant="warning">Initialisation de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'InitSession',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
async asyncData(context) {
// registo
console.log('[index asyncData started]')
try {
// iniciar uma sessão jSON
const dao = context.app.$dao()
const response = await dao.initSession()
// registo
console.log('[index asyncData response=]', response)
// recuperação do cookie de sessão PHP para as próximas solicitações
const phpSessionCookie = dao.getPhpSessionCookie()
// o cookie de sessão PHP é guardado na sessão [nuxt]
context.store.commit('replace', { phpSessionCookie })
// Houve algum erro?
if (response.état !== 700) {
// o erro encontra-se em response.réponse
throw new Error(response.réponse)
}
// regista-se que a sessão jSON foi iniciada
context.store.commit('replace', { jsonSessionStarted: true })
// apresenta-se o resultado
return { result: '[succès]' }
} catch (e) {
// registo
console.log('[index asyncData error=]', e)
// regista-se que a sessão jSON não foi iniciada
context.store.commit('replace', { jsonSessionStarted: false })
// é sinalizado o erro
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// guarda-se o registo
const session = context.app.$session()
session.save(context)
// registo
console.log('[index asyncData finished]')
}
},
// ciclo de vida
beforeCreate() {
console.log('[index beforeCreate]')
},
created() {
console.log('[index created]')
},
beforeMount() {
console.log('[index beforeMount]')
},
mounted() {
console.log('[index mounted]')
// apenas cliente
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- linha 7: a página apresenta o resultado [result] de uma consulta assíncrona (linhas 46 e 51);
- linha 31: a operação assíncrona consiste na abertura de uma sessão jSON com o servidor de cálculo de impostos;
- linha 25: sabe-se que, quando a página é solicitada diretamente ao servidor [nuxt], a função [asyncData] só é executada pelo servidor e não pelo cliente [nuxt], que é executado quando o navegador recebe a resposta do servidor [nuxt];
- linha 30: recupera-se a camada [dao] no contexto do servidor [nuxt];
- linha 35: se o servidor ainda não tiver feito qualquer pedido ao servidor de cálculo de impostos, recebe o seu primeiro cookie de sessão PHP; caso contrário, recebe o último cookie de sessão PHP que recebeu (consulte o código da camada [dao] do servidor [nuxt] no parágrafo com o link);
- linha 37: este cookie de sessão PHP é armazenado no store;
- linhas 39-42: verifica-se se a operação foi bem-sucedida. Caso contrário, é lançada uma exceção que será interceptada pelo [catch] da linha 47;
- linha 44: regista-se no store que a sessão jSON com o servidor PHP foi iniciada;
- linha 46: devolve-se o resultado [result], que é apresentado na linha 7;
- linhas 47-54: trata-se de uma eventual exceção. Esta pode ser de dois tipos:
- a operação HTTP da linha 31 falhou devido a um erro de comunicação entre o servidor [nuxt] e o servidor PHP;
- a operação HTTP da linha 31 foi bem-sucedida, mas o resultado recebido indicou um erro (linhas 39-42);
- linha 51: verifica-se que a sessão jSON com o servidor PHP não foi iniciada;
- linha 53: é devolvido o resultado [result], que é apresentado na linha 7. Além disso, definem-se as propriedades [showErrorLoading] e [errorLoadingMessage] que o cliente [nuxt] irá utilizar para apresentar uma mensagem de erro quando receber a página enviada pelo servidor [nuxt] (linhas 72-79);
- linhas 54-60: código executado em todos os casos (sucesso ou falha);
- linha 56: recupera-se a sessão [nuxt] no contexto do servidor [nuxt];
- linha 57: guarda-se a sessão;
- linhas 63-68: assim que a função [asyncData] terminar, o servidor [nuxt] executa as funções [beforeCreate] e [create];
Nota: a execução da página [index] pelo servidor [nuxt] pode falhar, por exemplo, se o servidor de cálculo de impostos não estiver em execução quando a aplicação [nuxt] for iniciada:

Neste caso, a única solução é iniciar o servidor de cálculo de impostos e, em seguida, a própria aplicação [nuxt], uma vez que o menu de navegação não oferece qualquer opção para iniciar uma sessão jSON com o servidor de cálculo de impostos;
15.14. A página [index] executada pelo cliente [nuxt]
A página [index] só é executada pelo cliente [nuxt] depois de o servidor [nuxt] lha ter enviado. Este enviou-lhe as informações [result] e, eventualmente, [showErrorLoading] e [errorLoadingMessage].
Sabe-se que a função [asyncData] não será executada. Restam, então, as funções do ciclo de vida e, nomeadamente, a função [mounted]:
mounted() {
console.log('[index mounted]')
// apenas cliente
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
- o cliente [nuxt] integra automaticamente nas propriedades da página os elementos [result] e, eventualmente, [showErrorLoading, errorLoadingMessage] que lhe foram enviados pelo servidor [nuxt]:
- a propriedade [result] é apresentada na linha 7;
- as propriedades [showErrorLoading, errorLoadingMessage] são utilizadas pelo método [mounted]: na linha 4, verifica-se a propriedade [showErrorLoading]. Se for verdadeira, utiliza-se, na linha 6, o barramento de eventos do cliente [nuxt] para sinalizar que há uma mensagem de erro a ser apresentada;
- o evento [errorLoading], lançado na linha 6, é interceptado pela página [layouts/default] descrita no parágrafo «ligação»;
15.15. A página [authentification] executada pelo servidor [nuxt]
A página [authentification] é responsável por identificar um utilizador junto do servidor de cálculo do imposto. O seu código é o seguinte:
<!-- página de autenticação -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem-->
<b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Authentification',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
async asyncData(context) {
// registo
console.log('[authentification asyncData started]')
if (process.client) {
// início da espera pelo cliente [nuxt]
context.app.$eventBus().$emit('loading', true)
// sem erros
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// autenticação no servidor
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// registo
console.log('[authentification asyncData response=]', response)
// resultado
const userAuthenticated = response.état === 200
// regista-se se o utilizador está autenticado ou não
context.store.commit('replace', { userAuthenticated })
// o estado é guardado na sessão [nuxt]
const session = context.app.$session()
session.save(context)
// erro de autenticação?
if (!userAuthenticated) {
// o erro encontra-se em response.réponse
throw new Error(response.réponse)
}
// obtém-se o resultado
return { result: '[succès]' }
} catch (e) {
// o erro é assinalado
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// registo
console.log('[authentification asyncData finished]')
if (process.client) {
// fim da espera pelo cliente [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// ciclo de vida
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// apenas cliente
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- linha 7: a página apresenta o resultado [result] da consulta assíncrona [asyncData] das linhas 25-65;
- linhas 28-33: o servidor não executa estas linhas destinadas ao cliente [nuxt];
- linha 36: recupera-se a camada [dao] do servidor [nuxt];
- linha 37: efetua-se a autenticação junto do servidor de cálculo do imposto com os dados de identificação de teste [admin, admin], que são os únicos aceites pelo servidor de cálculo do imposto;
- linha 41: a operação de autenticação foi bem-sucedida apenas se a resposta for do estado 200;
- linha 43: coloca-se no store a propriedade [userAuthenticated];
- linhas 44-46: o store é guardado na sessão [nuxt];
- linhas 48-51: se a autenticação falhar, é lançada uma exceção com a mensagem de erro enviada pelo servidor de cálculo de impostos;
- caso contrário, na linha 53, é devolvido um resultado de sucesso que será apresentado na linha 7;
- linhas 54-57: em caso de erro, definem-se três propriedades da página [result, showErrorLoading, errorLoadingMessage]. A propriedade [result] será apresentada na linha 7. As três propriedades serão enviadas ao cliente [nuxt];
- linhas 60-63: não são executadas pelo servidor [nuxt];
- assim que o [asyncData] tiver devolvido o seu resultado, este é apresentado na linha 7. Em seguida, são executados os métodos [beforeCreate] (linhas 67-69) e [created] (linhas 70-72);
- e está concluído;
Nota: a execução da página [authentification] pelo servidor [nuxt] pode falhar, por exemplo, se a sessão jSON com o servidor de cálculo do imposto não tiver sido inicializada. Isto pode ser feito da seguinte forma:
- elimine o cookie de sessão PHP do seu navegador (para recomeçar do zero):

- inicie a aplicação [nuxt] enquanto o servidor de cálculo não tiver sido iniciado: receberá uma mensagem de erro;
- inicie o servidor de cálculo do imposto;
- introduza o URL [/authentification] diretamente na barra de endereços do navegador:

Neste caso, a única solução é, mais uma vez, recarregar a página [index].
15.16. A página [authentification] executada pelo cliente [nuxt]
Voltemos ao código da página:
<!-- página de autenticação -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem-->
<b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Authentification',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
async asyncData(context) {
// registo
console.log('[authentification asyncData started]')
if (process.client) {
// início da espera pelo cliente [nuxt]
context.app.$eventBus().$emit('loading', true)
// sem erros
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// autenticação no servidor
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// registo
console.log('[authentification asyncData response=]', response)
// resultado
const userAuthenticated = response.état === 200
// regista-se se o utilizador está autenticado ou não
context.store.commit('replace', { userAuthenticated })
// o estado é guardado na sessão [nuxt]
const session = context.app.$session()
session.save(context)
// erro de autenticação?
if (!userAuthenticated) {
// o erro encontra-se em response.réponse
throw new Error(response.réponse)
}
// apresenta o resultado
return { result: '[succès]' }
} catch (e) {
// o erro é sinalizado
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// registo
console.log('[authentification asyncData finished]')
if (process.client) {
// fim da espera pelo cliente [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// ciclo de vida
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// apenas cliente
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
Existem dois casos de execução da página [authentification] pelo cliente [nuxt]:
- o cliente [nuxt] é executado depois de o servidor [nuxt] ter enviado ao navegador do cliente [nuxt] a página [authentification];
- o cliente [nuxt] é iniciado porque o utilizador clicou na ligação [Authentification] do menu de navegação:

Analisemos primeiro o primeiro caso. Neste caso, o cliente [nuxt] não executa a função [asyncData]. Incorpora nas propriedades da página os elementos [result] e, eventualmente, [showErrorLoading, errorLoadingMessage] que lhe foram enviados pelo servidor [nuxt]:
- a propriedade [result] é apresentada na linha 7;
- as propriedades [showErrorLoading, errorLoadingMessage] são utilizadas pelo método [mounted]: na linha 79, verifica-se a propriedade [showErrorLoading]. Se for verdadeira, utiliza-se, na linha 81, o barramento de eventos do cliente [nuxt] para sinalizar que existe uma mensagem de erro a apresentar;
O mecanismo de exibição da mensagem de erro foi explicado para a página [index] no parágrafo «ligação».
O caso 2 é o do cliente [nuxt] executado quando o utilizador clica na ligação [Authentification]. Neste caso, o cliente [nuxt] é executado de forma autónoma e não após o servidor [nuxt]. A função [asyncData] é então executada. Apresentamos apenas os detalhes que diferem das explicações fornecidas para a página executada pelo servidor [nuxt]:
- linhas 28-33: o cliente [nuxt] solicita a exibição da mensagem de espera e o desaparecimento de qualquer mensagem de erro que tenha sido exibida anteriormente;
- linha 36: é agora a camada [dao] do cliente [nuxt] que é obtida aqui;
- linhas 60-63: o cliente [nuxt] solicita o fim da exibição da mensagem de espera;
- assim que o [asyncData] terminar, o ciclo de vida da página terá início. A função [mounted] das linhas 76-83 será executada. Se tiver ocorrido um erro, a mensagem de erro será então exibida;
Nota: para provocar um erro, siga o procedimento explicado para o servidor [nuxt] no final do parágrafo com o link, mas, em vez de aceder à página [authentification] digitando o seu URL na barra de endereços, utilize o link [Authentification] do menu de navegação. É então o cliente [nuxt] que é executado.
15.17. A página [get-admindata]
O código da página [get-admindata] é o seguinte:
<!-- visualização get-admindata -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem -->
<b-alert slot="right" show variant="secondary"> Demande de [adminData] au serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'GetAdmindata',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
async asyncData(context) {
// registo
console.log('[get-admindata asyncData started]')
if (process.client) {
// início da espera
context.app.$eventBus().$emit('loading', true)
// sem erros
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// solicitação de dados [admindata]
const response = await context.app.$dao().getAdminData()
// registo
console.log('[get-admindata asyncData response=]', response)
// resultado
const adminData = response.état === 1000 ? response.réponse : ''
// os dados são colocados no armazenamento
context.store.commit('replace', { adminData })
// guardamos o armazenamento na sessão [nuxt]
const session = context.app.$session()
session.save(context)
// houve algum erro?
if (!adminData) {
// o erro encontra-se em response.réponse
throw new Error(response.réponse)
}
// retorna-se o valor recebido
return { result: adminData }
} catch (e) {
// notifica-se o erro
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// registo
console.log('[get-admindata asyncData finished]')
if (process.client) {
// fim da espera
context.app.$eventBus().$emit('loading', false)
}
}
},
// ciclo de vida
beforeCreate() {
console.log('[get-admindata beforeCreate]')
},
created() {
console.log('[get-admindata created]')
},
beforeMount() {
console.log('[get-admindata beforeMount]')
},
mounted() {
console.log('[get-admindata mounted]')
// cliente
if (this.showErrorLoading) {
console.log('[get-admindata mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
Esta página é muito semelhante à página [authentification]. As explicações são análogas tanto para a sua execução pelo servidor [nuxt] como para a sua execução pelo cliente [nuxt]. Note-se, no entanto, que a linha 7 não apresenta «sucesso»/«falha», como anteriormente, mas sim o valor dos dados recebidos do servidor de cálculo do imposto (linha 52):

O resultado acima é obtido tanto com o servidor como com o cliente [nuxt]. Para provocar um erro, solicite a página [get-admindata], através do servidor ou do cliente [nuxt], sem estar autenticado:

15.18. A página [fin-session]
O código da página é o seguinte:
<!-- página principal -->
<template>
<Layout :left="true" :right="true">
<!-- navegação -->
<Navigation slot="left" />
<!-- mensagem-->
<b-alert slot="right" show variant="warning">Fin de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'FinSession',
// componentes utilizados
components: {
Layout,
Navigation
},
// dados assíncronos
async asyncData(context) {
// registo
console.log('[fin-session asyncData started]')
// 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 {
// é solicitada uma nova sessão PHP ao servidor de cálculo de impostos
const dao = context.app.$dao()
const response = await dao.finSession()
// registo
console.log('[fin-session asyncData response=]', response)
// houve algum erro?
if (response.état !== 400) {
// o erro encontra-se em response.réponse
throw new Error(response.réponse)
}
// o servidor enviou um novo cookie de sessão PHP
// este é recuperado tanto pelo servidor como pelo cliente Nuxt
// se este código for executado pelo cliente [nuxt], o cookie de sessão PHP deve ser inserido na sessão do Nuxt
// para que o plugin [plgDao] do servidor [nuxt] possa recuperá-lo e inicializar a camada [dao] com
//. Se este código for executado pelo servidor [nuxt], o cookie de sessão PHP deve ser inserido na sessão do Nuxt
// para que o roteamento do cliente [nuxt] o recupere e o transmita ao navegador
const phpSessionCookie = dao.getPhpSessionCookie()
// regista-se no armazenamento o facto de a sessão jSON ter sido iniciada e memoriza-se o cookie de sessão PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// o armazenamento é guardado na sessão [nuxt]
const session = context.app.$session()
session.save(context)
// retorna-se o resultado
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// registo
console.log('[fin-session asyncData error=]', e)
// é sinalizado o erro
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// registo
console.log('[fin-session asyncData finished]')
if (process.client) {
// fim da espera
context.app.$eventBus().$emit('loading', false)
}
}
},
// ciclo de vida
beforeCreate() {
console.log('[fin-session beforeCreate]')
},
created() {
console.log('[fin-session created]')
},
beforeMount() {
console.log('[fin-session beforeMount]')
},
mounted() {
console.log('[fin-session mounted]')
// apenas cliente
if (this.showErrorLoading) {
console.log('[fin-session mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
O código é muito semelhante ao das páginas anteriores e as explicações são as mesmas. Basta apenas destacar um ponto: a operação assíncrona da linha 38 faz com que o servidor de cálculo do imposto envie um novo cookie de sessão PHP. As explicações relativas à gestão deste cookie diferem consoante seja o servidor ou o cliente [nuxt] a executar este código.
Comecemos pelo servidor [nuxt]:
- linha 37: é a camada [dao] do servidor [nuxt] que é instanciada. Recorde-se o código do seu construtor:
// construtor
constructor(axios, phpSessionCookie) {
// biblioteca axios
this.axios = axios
// valor do cookie de sessão
this.phpSessionCookie = phpSessionCookie
// nome do cookie de sessão do servidor PHP
this.phpSessionCookieName = 'PHPSESSID'
}
Vemos na linha 1 que o construtor necessita do cookie de sessão PHP do momento, o último recebido, seja pelo servidor ou pelo cliente [nuxt];
- linha 52: o servidor [nuxt] recupera o cookie da nova sessão PHP ou, caso a operação de encerramento da sessão tenha falhado, o cookie anterior;
- linha 54: o cookie de sessão PHP é colocado no armazenamento e, em seguida, guardado na sessão [nuxt] nas linhas 56-57;
- depois do servidor, é o cliente [nuxt] que executa a página [fin-session] com os dados enviados pelo servidor. Sabe-se que não irá executar a função [asyncData];
- por fim, depois de o servidor e o cliente [nuxt] terem concluído o seu trabalho, sabemos que o cookie PHP, necessário para as trocas de dados com o servidor de cálculo do imposto, se encontra na sessão [nuxt];
O facto de o cookie PHP estar na sessão [nuxt] é suficiente para o servidor, pois é daí que a sua camada [dao] o irá obter. No plugin [server/plgDao], que inicializa a camada [dao] do servidor, foi escrito:
/* eslint-disable no-console */
// cria-se um ponto de acesso à camada [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// configuração do axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// recuperamos o cookie de sessão
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instanciação da camada [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// injeção de uma função [$dao] no contexto
inject('dao', () => dao)
// registo
console.log('[fonction server $dao créée]')
}
- na linha 13, a camada [dao] do servidor [nuxt] é instanciada com o cookie de sessão PHP obtido da sessão [nuxt], linhas 9-10;
Para o cliente [nuxt], a situação é diferente. Na verdade, não é ele que envia o cookie, mas sim o navegador que o executa. No entanto, este navegador não reconhece o cookie da nova sessão PHP recebido pelo servidor [nuxt]. Se utilizarmos os links do menu de navegação [3]:

O servidor de cálculo do imposto receberá do navegador um cookie de sessão PHP obsoleto e responderá que a esse cookie não está associada nenhuma sessão jSON. Temos de encontrar uma forma de enviar ao navegador o novo cookie de sessão PHP.
Para tal, podemos utilizar um middleware de encaminhamento:

O script [client/routing] é o middleware de encaminhamento declarado no ficheiro [nuxt.config]:
// roteador
router: {
// raiz dos URL da aplicação
base: '/nuxt-12/',
// middleware de encaminhamento
middleware: ['routing']
},
O script [middleware/routing] é o seguinte:
/* eslint-disable no-console */
// importamos o middleware do cliente
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.client) {
// roteamento do cliente
clientRouting(context)
}
}
- linhas 9-12: apenas se encaminha o cliente com uma função importada na linha 4;
O script [middleware/client/routing] é o seguinte:
/* 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
}
}
Voltemos à situação imediatamente após a execução da página [fin-session] pelo servidor [nuxt]:

Se clicarmos num dos links do menu [3], o cliente [nuxt] assumirá o controlo. Como vai ocorrer uma mudança de página, o script de encaminhamento do cliente será executado:
- linha 13: o cookie de sessão PHP é encontrado no armazenamento da aplicação [nuxt];
- linha 14: se não estiver vazio, é transmitido ao navegador (linha 16). A partir desse momento, o navegador do cliente [nuxt] dispõe do cookie de sessão correto PHP;
O script [client/routing] é executado sempre que o cliente [nuxt] muda de página. O código do script é válido independentemente da página de destino: na maioria das vezes, limita-se a atribuir ao navegador um cookie de sessão PHP que este já possui, exceto em dois casos:
- imediatamente após o arranque da aplicação, o servidor [nuxt] executa a página [index] e recebe um primeiro cookie de sessão PHP que o navegador do cliente [nuxt] não possui;
- quando o servidor [nuxt] executa a página [fin-session], tal como acabou de ser explicado;
Agora, analisemos o caso em que a página [fin-session] é executada apenas pelo cliente [nuxt], porque se clicou no respetivo link no menu de navegação. É agora o cliente [nuxt] que executa a função [asyncData]:
try {
// solicita-se uma nova sessão PHP ao servidor de cálculo do imposto
const dao = context.app.$dao()
const response = await dao.finSession()
// registo
console.log('[fin-session asyncData response=]', response)
// ocorreu algum erro?
if (response.état !== 400) {
// o erro encontra-se em response.réponse
throw new Error(response.réponse)
}
// o servidor enviou um novo cookie de sessão PHP
// este é recuperado tanto pelo servidor como pelo cliente Nuxt
// se este código for executado pelo cliente [nuxt], o cookie de sessão PHP deve ser inserido na sessão do Nuxt
// para que o plugin [plgDao] do servidor [nuxt] possa recuperá-lo e inicializar a camada [dao] com
//. Se este código for executado pelo servidor [nuxt], o cookie de sessão PHP deve ser inserido na sessão do Nuxt
// para que o roteamento do cliente [nuxt] o recupere e o transmita ao navegador
const phpSessionCookie = dao.getPhpSessionCookie()
// regista-se no armazenamento o facto de a sessão jSON ter sido iniciada e memoriza-se o cookie de sessão PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// o armazenamento é guardado na sessão [nuxt]
const session = context.app.$session()
session.save(context)
// retorna-se o resultado
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// registo
console.log('[fin-session asyncData error=]', e)
// é sinalizado o erro
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// registo
console.log('[fin-session asyncData finished]')
if (process.client) {
// fim da espera
context.app.$eventBus().$emit('loading', false)
}
}
- linha 3: é a camada [dao] do cliente [nuxt] que é obtida aqui;
- linha 18: o cookie de sessão PHP, recuperado pela camada [dao] do cliente [nuxt], é memorizado, colocado no armazenamento (linha 20) e, em seguida, guardado na sessão [nuxt] (linhas 22-23);
- a partir daí, tudo corre bem, pois sabemos que a camada [dao] do servidor [nuxt] irá buscar o cookie de sessão PHP na sessão [nuxt];
15.19. Exécution
Para executar este exemplo, é necessário ter o cuidado de, 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:

15.20. Conclusion
Este exemplo revelou-se particularmente complexo. Reuniu conhecimentos adquiridos nos exemplos anteriores: persistência do store numa sessão [nuxt], plugins de injeção de funções, middleware de encaminhamento e gestão de erros em operações assíncronas. A complexidade foi aumentada pelo facto de querermos que o utilizador pudesse tanto utilizar os links do menu de navegação como digitar URL manualmente, sem que isso causasse falhas na aplicação. Para tal, fomos obrigados a analisar o comportamento de cada página, consoante fosse executada pelo cliente ou pelo servidor [nuxt].
Esta uniformização do comportamento do cliente e do servidor [nuxt] não é indispensável. Podemos considerar o caso frequente em que:
- a primeira página é fornecida pelo servidor [nuxt];
- todas as páginas seguintes são fornecidas pelo cliente [nuxt], que funciona então no modo [SPA];
No entanto, mesmo neste caso, é necessário verificar o resultado da execução de todas as páginas pelo servidor [nuxt], pois é isso que os motores de busca irão obter quando as solicitarem.