15. Exemplo [nuxt-12]: Pedidos HTTP com axios
15.1. Introdução
Neste novo exemplo, vamos explorar como fazer pedidos HTTP utilizando a biblioteca [axios] dentro de funções [asyncData]. Além disso, vamos aplicar conceitos já abordados:
- a utilização de plugins do exemplo [nuxt-06]:
- armazenamento do store num cookie de sessão do exemplo [nuxt-06];
- controlo de navegação utilizando middleware do exemplo [nuxt-09];
- gestão de erros do exemplo [nuxt-11];
A arquitetura do exemplo será a seguinte:

- A aplicação [nuxt] será hospedada no servidor [node.js] [3], descarregada pelo navegador [1], que a executará;
- tanto o cliente [nuxt] [1] como o servidor [nuxt] [3] farã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 pedidos CORS ativados;
A arquitetura do exemplo pode ser simplificada da seguinte forma:

- Em [1], o servidor [node.js] entrega as páginas [nuxt] ao navegador [2]. É a camada [web] [8] do servidor que entrega 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;
- Com cada pedido de página ao servidor [Node.js] [1], o navegador [2] recebe toda a aplicação [Nuxt], que passa então a ser executada no modo SPA. O bloco [UI] (Interface do Utilizador) [4] apresenta as páginas [Vue.js] ao utilizador. As ações do utilizador ou o ciclo de vida natural das páginas podem desencadear pedidos de dados externos ao servidor de dados [3]. A camada [DAO] [5] efetua então as solicitações HTTP necessárias;
15.2. Estrutura do diretório do projeto

15.3. O ficheiro de configuração [nuxt.config.js]
O projeto será controlado pelo seguinte ficheiro [nuxt.config.js]:
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',
// Doc: 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) { }
},
// source code directory
srcDir: 'nuxt-12',
// router
router: {
// application URL root
base: '/nuxt-12/',
// routing middleware
middleware: ['routing']
},
// server
server: {
// service port, default 3000
port: 81,
// network addresses listened to, default localhost: 127.0.0.1
// 0.0.0.0 = all the machine's network addresses
host: 'localhost'
},
// environment
env: {
// axios configuration
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// session cookie configuration [nuxt]
maxAge: 60 * 5
}
}
- linha 22: tratamos nós próprios da notificação de conclusão de uma ação assíncrona;
- linha 31: utilizaremos vários plugins especializados para o cliente ou para o servidor, mas não para ambos ao mesmo tempo;
- linha 52: o módulo [axios] está integrado no [nuxt]. Como resultado, o objeto [axios], que irá tratar dos pedidos HTTP da aplicação [nuxt] para o servidor de cálculo de impostos em PHP, estará disponível em [context.$axios];
- linha 54: o módulo [cookie-universal-nuxt] 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 opção, preferindo, em vez disso, a propriedade [env] da linha 88;
- linha 90: tempo máximo de espera por uma resposta do servidor de cálculo de impostos;
- linha 91: obrigatório para o cliente [nuxt] — permite a utilização de cookies nas comunicações com o servidor de cálculo de impostos;
- linha 92: o URL base do servidor de cálculo de impostos;
- linha 94: tempo de vida da sessão Nuxt (5 min);
- linha 77: a navegação do cliente e do servidor [nuxt] será controlada pelo middleware de roteamento;
15.4. A camada [UI] da aplicação

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

- em [2], o menu que dá acesso à API do servidor de cálculo de impostos:
- [Autenticação]: corresponde à página [authentication]. Esta página envia um pedido de autenticação ao servidor de cálculo de impostos utilizando as credenciais [admin, admin], que são atualmente as únicas autorizadas. O resultado apresentado é semelhante ao de [3];
- [Pedido de AdminData]: corresponde à página [get-admindata]. Esta página solicita dados ao servidor de cálculo de impostos — aqui referidos como [adminData] — que permitem o cálculo de impostos. O resultado apresentado é semelhante ao de [3];
- [End Tax Session]: corresponde à página [end-session]. Esta página envia um pedido PHP de fim de sessão ao servidor de cálculo de impostos. O servidor cancela então a sessão PHP atual e inicializa uma nova sessão em branco;
15.5. As camadas [dao] da aplicação [nuxt]
Conforme mencionado acima, a arquitetura da aplicação [nuxt] será a seguinte:

- Em [1], o servidor [node.js] entrega as páginas [nuxt] ao navegador [2]. É a camada [web] [8] do servidor que entrega 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 os pedidos HTTP necessários;
- Com cada pedido de página ao servidor [Node.js] [1], o navegador [2] recebe toda a aplicação [Nuxt], que passa então a funcionar no modo SPA. O bloco [UI] (Interface do Utilizador) [4] apresenta as páginas [Vue.js] ao utilizador. As ações do utilizador ou o ciclo de vida da página podem desencadear pedidos de dados externos ao servidor de dados [3]. A camada [DAO] [5] efetua então os pedidos HTTP necessários;
Iremos utilizar a versão 14 do servidor de cálculo de impostos desenvolvido no documento |Introdução ao PHP7 através de Exemplos|. Iremos utilizar apenas parte da sua API JSON (Interface de Programação de Aplicações):
Pedido | Resposta |
| |
| |
| |
| |
15.5.1. A camada [DAO] do servidor [Nuxt]

O servidor [node.js] [1] utilizará a camada [DAO] descrita no documento |Introdução à estrutura VUE.JS através de exemplos|. Aqui está o código novamente:
'use strict';
// imports
import qs from 'qs'
class Dao {
// manufacturer
constructor(axios) {
this.axios = axios;
// session cookie
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// init session
async initSession() {
// query options HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: 'init-session',
type: 'json'
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// query options HHTP [post /main.php?action=authenticate-user]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// body of POST
data: qs.stringify({
user: user,
password: password
}),
// URL parameters
params: {
action: 'authentifier-utilisateur'
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// query options HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// URL parameters
params: {
action: 'get-admindata'
}
};
// execute query HTTP
const data = await this.getRemoteData(options);
// result
return data;
}
async getRemoteData(options) {
// for the session cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execute query HTTP
let response;
try {
// asynchronous request
response = await this.axios.request('main.php', options);
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response;
} else {
// error restart
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// retrieve the session cookie if it exists
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie is an array
// look for the session cookie in this table
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// look for the session cookie
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// the session cookie is stored
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// we found
trouvé = true;
} else {
// next item
i++;
}
}
}
// the server response is in [response.data]
return response.data;
}
}
// class export
export default Dao;
- Todos os métodos na camada [dao] devolvem o objeto enviado pelo servidor de dados [{action: ‘xx’, status: nn, response: {...}] com:
- [action]: o nome da ação executada pelo servidor de dados;
- [state]: indicador numérico:
- [initSession]: status=700 para uma resposta sem erros;
- [authenticateUser]: status=200 para uma resposta sem erros;
- [getAdminData]: status=1000 para uma resposta sem erros;
- [end-session]: status=400 para uma resposta sem erros;
- [response]: resposta associada ao indicador numérico [status]. Pode variar dependendo deste indicador numérico;
Vamos examinar o construtor da classe [Dao]:
// constructeur
constructor(axios) {
this.axios = axios;
// cookie de session
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- linha 2: o objeto [axios] passado como argumento ao construtor é fornecido pelo código de chamada. É este objeto que irá efetuar as solicitações HTTP;
- linha 5: o nome do cookie de sessão enviado pelo servidor de dados escrito em PHP;
- linha 6: o cookie de sessão trocado entre a camada [dao] e o servidor de dados. Este é inicializado pela função [getRemoteData] nas linhas 67–113;
Para o cookie de sessão, precisamos de considerar duas camadas [dao] distintas:
- a camada do navegador;
- a camada do servidor;
Teremos de gerir três cookies de sessão:
- o que é trocado entre o cliente [nuxt] e o servidor PHP 7;
- aquele trocado entre o servidor [nuxt] e o servidor PHP 7;
- o que é trocado entre o cliente [nuxt] e o servidor [nuxt];
Iremos garantir que o cookie de sessão com o servidor PHP é o mesmo tanto para o cliente como para o servidor [nuxt]. Iremos chamar a este cookie o cookie de sessão PHP. Este cookie é o dos casos 1 e 2. Iremos chamar ao cookie do caso 3 o cookie de sessão [nuxt]. 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];
Porquê utilizar o mesmo cookie para as sessões PHP do cliente e as sessões do navegador [nuxt]? Queremos que a aplicação seja capaz de comunicar com o servidor PHP 7, independentemente de ser o cliente ou o servidor [nuxt]:
- se uma ação A do servidor [nuxt] colocar o servidor PHP no estado E, este estado é refletido na sessão PHP mantida pelo servidor PHP;
- ao utilizar o mesmo cookie de sessão PHP que o servidor, uma ação B do cliente [nuxt] que se siga à ação A do servidor [nuxt] encontraria o servidor PHP no estado E deixado pelo servidor [nuxt] e poderia, portanto, dar continuidade ao 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 antes, esta ação poderá basear-se no trabalho realizado pela ação B do cliente [nuxt];
Para permitir que o navegador do cliente [nuxt] comunique com o servidor PHP de cálculo de impostos, utilizaremos a versão 14 deste servidor, que permite chamadas entre domínios — ou seja, chamadas de um navegador para o servidor PHP. As chamadas do servidor [nuxt] para o servidor PHP, no entanto, não são chamadas entre domínios. Este conceito aplica-se apenas a chamadas efetuadas a partir de um navegador.
Voltemos ao código do construtor da classe [Dao] anterior:
// constructeur
constructor(axios) {
this.axios = axios;
// cookie de session
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- As linhas 5 e 6 correspondem ao cookie de sessão PHP com o servidor de cálculo de impostos;
A gestão do cookie de sessão PHP acima não é adequada para o servidor [nuxt]: a sua camada [dao] é instanciada com cada novo pedido feito ao servidor [nuxt]. Recorde-se que solicitar uma página ao servidor [nuxt] reinicia efetivamente a aplicação [nuxt]. Assim, quando o servidor [nuxt] faz a sua primeira solicitação ao servidor de dados, o cookie de sessão PHP para a camada [dao] é inicializado; este valor é perdido durante a próxima solicitação HTTP do mesmo servidor [nuxt], porque, entretanto, a sua camada [dao] foi recriada, o construtor foi reexecutado e o cookie de sessão PHP foi reiniciado para uma string vazia (linha 6);
Uma solução consiste em utilizar um construtor diferente para a camada [dao] do servidor:
// constructeur
constructor(axios, phpSessionCookie) {
// bibliothèque axios
this.axios = axios
// valeur du cookie de session
this.phpSessionCookie = phpSessionCookie
// nom du cookie de session du serveur 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] 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] faz 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 isso, reenvia-o automaticamente com cada nova solicitação ao servidor [nuxt];
- quando o servidor [nuxt] precisa de fazer uma nova solicitação ao servidor PHP, encontra o cookie de sessão PHP dentro do cookie de sessão [nuxt] que o navegador lhe enviou. Em seguida, envia-o para o servidor PHP;
Existem, de facto, dois cookies de sessão, e estes não devem ser confundidos:
- 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. Vamos adicionar esta:
// end of tax calculation session
async finSession() {
// query options HHTP [get /main.php?action=end-session]
const options = {
method: 'GET',
// URL parameters
params: {
action: 'fin-session'
}
}
// execute query HTTP
const data = await this.getRemoteData(options)
// result
return data
}
Durante os testes, descobrimos que a função [getRemoteData] chamada na linha 12 não é adequada para o método [finSession]:
async getRemoteData(options) {
// for the session cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// execute query HTTP
let response;
try {
// asynchronous request
response = await this.axios.request('main.php', options);
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response;
} else {
// error restart
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// retrieve the session cookie if it exists
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie is an array
// look for the session cookie in this table
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// look for the session cookie
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// the session cookie is stored
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// we found
trouvé = true;
} else {
// next item
i++;
}
}
}
// the server response is in [response.data]
return response.data;
}
- linhas 30–43: procuramos o cookie [PHPSESSID=xxx]. Se encontrado, é armazenado na classe (linha 36);
Este código não é adequado para o novo método [finSession] porque, na ação [fin-session], o servidor PHP envia dois cookies denominados [PHPSESSID]. Aqui está um exemplo obtido utilizando 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 primeiro que eliminou a sessão PHP atual;
- em [6], o servidor PHP envia o cookie para a nova sessão PHP;
Com o código atual, a função [getRemoteData] recupera o cookie [5], quando é o cookie [6] que precisa de ser armazenado.
Devemos, portanto, atualizar o código da função [getRemoteData]:
async getRemoteData(options) {
// is there a PHP session cookie?
if (this.phpSessionCookie) {
// are there headers?
if (!options.headers) {
// create an empty object
options.headers = {}
}
// session cookie header PHP
options.headers.Cookie = this.phpSessionCookie
}
// execute query HTTP
let response
try {
// asynchronous request
response = await this.axios.request('main.php', options)
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response
} else {
// error restart
throw error
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// look for session cookie PHP in received cookies
// all cookies received
const cookies = response.headers['set-cookie']
if (cookies) {
// cookies is a picture
// look for the PHP session cookie in this array
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// look for the PHP session cookie
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// we store the PHP session cookie
const phpSessionCookie = results[1]
// is the word [deleted] in it?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// we have the right session cookie PHP
this.phpSessionCookie = phpSessionCookie
// we found
trouvé = true
} else {
// next item
i++
}
} else {
// next item
i++
}
}
}
// the server response is in [response.data]
return response.data
}
- Linha 41: Encontrámos um cookie chamado [PHPSESSID]. Guardamo-lo localmente;
- linha 43: verificamos se o cookie guardado contém a string [PHPSESSID=deleted];
- linha 46: se a resposta for não, então encontrámos o cookie correto [PHPSESSID]. Guardamo-lo na classe;
Após a função [getRemoteData], o cookie de sessão PHP é armazenado na classe, em [this.phpSessionCookie]. Mencionámos que a classe é instanciada com cada novo pedido HTTP do servidor [nuxt]. O cookie de sessão PHP deve, portanto, ser extraído da classe. Para tal, adicionamos-lhe um novo método:
// accès au cookie de la session 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 exista;
- Assim que a ação estiver concluída, o servidor [nuxt] recupera o cookie de sessão PHP armazenado pela camada [dao] utilizando o método [getPhpSessionCookie] anterior. Este cookie pode ser igual ao anterior ou diferente. O último caso ocorre em duas ocasiões:
- quando o método [initSession] é executado (não havia nenhum cookie de sessão PHP anteriormente);
- quando o método [finSession] é executado (o servidor PHP altera o cookie de sessão PHP);
Note uma peculiaridade relativa ao cookie de sessão PHP. O servidor [nuxt] nem sempre recebe este cookie do servidor PHP. Na verdade, o servidor PHP envia-o apenas uma vez. Depois disso, deixa de o enviar. Ao analisar o código de [getRemoteData] e [getPhpSessionCookie], podemos ver que, quando o servidor PHP não envia um cookie de sessão, a função [getPhpSessionCookie] devolve o cookie de sessão PHP fornecido ao construtor. É assim que o servidor [nuxt] 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] em execução num navegador, usamos o código da classe [Dao] no documento |Introdução à estrutura VUE.JS através de exemplos|:
"use strict";
// imports
import qs from "qs";
class Dao {
// manufacturer
constructor(axios) {
this.axios = axios;
}
// init session
async initSession() {
// query options HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: "init-session",
type: "json"
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// query options HHTP [post /main.php?action=authenticate-user]
const options = {
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded"
},
// body of POST
data: qs.stringify({
user: user,
password: password
}),
// URL parameters
params: {
action: "authentifier-utilisateur"
}
};
// execute query HTTP
return await this.getRemoteData(options);
}
async getAdminData() {
// query options HHTP [get /main.php?action=get-admindata]
const options = {
method: "GET",
// URL parameters
params: {
action: "get-admindata"
}
};
// execute query HTTP
const data = await this.getRemoteData(options);
// result
return data;
}
async getRemoteData(options) {
// execute query HTTP
let response;
try {
// asynchronous request
response = await this.axios.request("main.php", options);
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response;
} else {
// error restart
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// the server response is in [response.data]
return response.data;
}
}
// class export
export default Dao;
Este código difere da camada [dao] do servidor [nuxt] na medida em que não gere o cookie de sessão PHP com o servidor de cálculo de impostos: é o navegador que trata disso.
Tal como fizemos para a camada [dao] do servidor [nuxt], vamos adicionar um método [finSession]:
// end of tax calculation session
async finSession() {
// query options HHTP [get /main.php?action=end-session]
const options = {
method: 'GET',
// URL parameters
params: {
action: 'fin-session'
}
}
// execute query HTTP
const data = await this.getRemoteData(options)
// result
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 lida com a situação corretamente: mantém apenas o cookie da nova sessão PHP iniciada pelo servidor de cálculo de impostos. Assim, da próxima vez que o cliente [nuxt] enviar um pedido ao servidor PHP, o cookie de sessão PHP estará correto, porque é o navegador que o está a enviar. No entanto, há um problema: o servidor [nuxt] não tem conhecimento de que o cookie de sessão PHP mudou. Nas suas comunicações com o servidor PHP, irá então enviar um cookie de sessão PHP que já não existe, e isto irá causar problemas. O cliente [nuxt] precisa de notificar o servidor [nuxt] de que o cookie de sessão PHP mudou e transmiti-lo ao servidor. Sabemos como isso pode ser feito: 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 usar a segunda solução porque já está pronta a funcionar. O método [getRemoteData] do cliente [nuxt] passa então a ser o seguinte:
async getRemoteData(options) {
// execute query HTTP
let response
try {
// asynchronous request
response = await this.axios.request('main.php', options)
} catch (error) {
// the [error] parameter is an exception instance - it can take various forms
if (error.response) {
// the server response is in [error.response]
response = error.response
} else {
// error restart
throw error
}
}
// response is the entire HTTP response from the server (HTTP headers + response itself)
// look for session cookie PHP in received cookies
// all cookies received
const cookies = response.headers['set-cookie']
if (cookies) {
// cookies is a picture
// look for session cookie PHP in this array
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// look for the PHP session cookie
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// we store the PHP session cookie
const phpSessionCookie = results[1]
// is the word [deleted] in it?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// we have the right session cookie PHP
this.phpSessionCookie = phpSessionCookie
// we found
trouvé = true
} else {
// next item
i++
}
} else {
// next item
i++
}
}
}
// the server response is in [response.data]
return response.data
}
Mantivemos apenas o código em [getRemoteData] que processa a resposta do servidor PHP para recuperar o cookie de sessão PHP. Não mantivemos o código que incluía o cookie de sessão PHP na solicitação ao servidor PHP, pois o navegador que hospeda o cliente [nuxt] trata disso.
Assim que o cookie de sessão PHP é obtido pelo cliente [nuxt], este deve ser colocado na sessão [nuxt] para que o servidor [nuxt] o possa utilizar. A camada [dao] não trata disto, mas fornece acesso, através de um método, ao cookie de sessão PHP que armazenou:
// accès au cookie de la session PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
A função [getPhpSessionCookie] nem sempre devolve um cookie de sessão válido:
- é importante lembrar aqui que a camada [dao] do cliente [nuxt] é persistente. É instanciada uma vez e permanece na memória;
- enquanto o servidor PHP não enviar um cookie de sessão PHP para o cliente [nuxt], a função [getPhpSessionCookie] do cliente [nuxt] retorna um valor [undefined];
- quando o servidor PHP envia um cookie de sessão PHP para o 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: não envia ela própria o cookie de sessão PHP, uma vez que o navegador trata disso. No entanto, optámos por manter duas camadas [dao] distintas porque o raciocínio por trás das respetivas implementações difere.
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 */
// définition de la session
const session = {
// contenu de la session
value: {
// store non initialisé
initStoreDone: false,
// valeur du store Vuex
store: ''
},
// sauvegarde de la session dans un cookie
save(context) {
// sauvegarde du store en session
this.value.store = context.store.state
console.log('nuxt-session save=', this.value)
// sauvegarde de la valeur de la session
context.app.$cookies.set('nuxt-session', this.value, { path: context.base, maxAge: context.env.maxAge })
},
// reset de la session
reset(context) {
console.log('nuxt-session reset')
// reset du store
context.store.commit('reset')
// sauvegarde du nouveau store en session et sauvegarde de la session
this.save(context)
}
}
// export de la session
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] é utilizado para guardar a sessão [nuxt] num cookie. Aqui, utilizamos a biblioteca [cookie-universal-nuxt] para gerir o cookie. Repare no nome do cookie de sessão [nuxt]: [nuxt-session] (linha 17);
- linhas 20–26: o método [reset] reinicia a sessão [nuxt];
- linha 23: o armazenamento Vuex é reiniciado e, em seguida, guardado na sessão na linha 25;
15.7. plugins de gestão de sessão [nuxt]

15.7.1. O plugin de gestão de sessão [nuxt] para o servidor [nuxt]
Quando a aplicação é iniciada, o servidor [nuxt] é executado primeiro. Por isso, é responsável por inicializar a sessão [nuxt]. O script [server/plgSession] é o seguinte:
/* eslint-disable no-console */
// import de la session
import session from '@/entities/session'
export default (context, inject) => {
// gestion de la session serveur
console.log('[plugin server plgSession]')
// y-a-t-il une session existante ?
const value = context.app.$cookies.get('nuxt-session')
if (!value) {
// nouvelle session
console.log("[plugin server plgSession], démarrage d'une nouvelle session")
} else {
// session existante
console.log("[plugin server plgSession], reprise d'une session existante")
session.value = value
}
// on injecte une fonction dans [context, Vue] qui rendra la session courante
inject('session', () => session)
}
- linha 4: importa o código de sessão [nuxt];
- linha 11: recuperar o valor do cookie de sessão [nuxt];
- linhas 12–15: Se o cookie de sessão [nuxt] não existisse, então a sessão [nuxt] importada na linha 4 é suficiente. Não há mais nada a fazer;
- linhas 15–19: Se o cookie de sessão [nuxt] existisse, então na linha 18 armazenamos o seu valor na sessão importada na linha 4;
- linha 22: a sessão foi inicializada ou restaurada. Disponibilizamo-la através da função [$session];
15.7.2. O plugin de gestão de sessão [nuxt] para o cliente [nuxt]
O script [client/plgSession] é o seguinte:
/* eslint-disable no-console */
// import de la session
import session from '@/entities/session'
export default (context, inject) => {
// gestion de la session client
console.log('[plugin client plgSession], reprise de la session [nuxt] du serveur')
// on récupère la session existante du serveur nuxt
session.value = context.app.$cookies.get('nuxt-session')
// on injecte une fonction dans [context, Vue] qui rendra la session courante
inject('session', () => session)
}
- linha 4: a sessão [nuxt] é importada;
- linha 10: recuperamos a sessão [nuxt] atual a partir do cookie [nuxt-session];
- linha 13: devolvemos a sessão [nuxt] importada na linha 4 através da função injetada [$session];
15.8. Plugins para as camadas [dao]

15.8.1. O plugin da camada [dao] para o cliente [nuxt]
O script [client/plgDao] é o seguinte:
/* eslint-disable no-console */
// on crée un point d'accès à la couche [Dao]
import Dao from '@/api/client/Dao'
export default (context, inject) => {
// configuration axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
context.$axios.defaults.withCredentials = context.env.withCredentials
// instanciation de la couche [dao]
const dao = new Dao(context.$axios)
// injection d'une fonction [$dao] dans le contexte
inject('dao', () => dao)
// log
console.log('[fonction client $dao créée]')
}
- linha 3: a camada [dao] do cliente [nuxt] é importada;
- linhas 6-8: configuramos o objeto [context.$axios], que fará pedidos HTTP para a camada [dao] do cliente [nuxt] utilizando informações do ficheiro [nuxt.config]:
// environnement
env: {
// configuration axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// configuration du cookie de session [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 fornece acesso à camada [dao] da linha 10;
Assim, para aceder à camada [dao] do cliente [nuxt] quando este está em execução, escrevemos:
- [context.app.$dao()] onde o contexto é conhecido;
- [this.$dao()] numa página [Vue.js];
15.8.2. O plugin da camada [dao] para o servidor [nuxt]
O script [server/plgDao] é o seguinte:
/* eslint-disable no-console */
// on crée un point d'accès à la couche [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// configuration axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// on récupère le cookie de session
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instanciation de la couche [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// injection d'une fonction [$dao] dans le contexte
inject('dao', () => dao)
// log
console.log('[fonction server $dao créée]')
}
- linha 3: a camada [dao] do servidor [nuxt] é importada;
- linhas 6-7: o objeto [context.$axios] é configurado para efetuar pedidos HTTP para a camada [dao] do servidor [nuxt] utilizando informações do ficheiro [nuxt.config]:
// environnement
env: {
// configuration axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// configuration du cookie de session [nuxt]
maxAge: 60 * 5
}
- linha 9: recuperar o armazenamento da aplicação [nuxt];
- linha 10: se o armazenamento existir, recuperamos o cookie de sessão PHP, pois precisamos dele para instanciar a camada [dao] do servidor [nuxt];
- linha 13: instanciar 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 fornece acesso à camada [dao] da linha 13;
Assim, para aceder à camada [dao] do servidor [nuxt] quando este está em execução, escrevemos:
- [context.app.$dao()] onde o contexto é conhecido;
- [this.$dao()] numa página [Vue.js];
15.9. O armazenamento Vuex
![]()
O armazenamento [Vuex] irá armazenar todos os dados que precisam de ser partilhados pelos vários componentes da aplicação [páginas, cliente, servidor] sem que esses dados sejam reativos.
/* eslint-disable no-console */
// awning status
export const state = () => ({
// session jSON started
jsonSessionStarted: false,
// authenticated user
userAuthenticated: false,
// session cookie PHP
phpSessionCookie: '',
// adminData
adminData: ''
})
// changes in the awning
export const mutations = {
// state replacement
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
},
// awning reset
reset() {
this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
}
}
// awning actions
export const actions = {
nuxtServerInit(store, context) {
// who executes this code?
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// init session
initStore(store, context)
}
}
function initStore(store, context) {
// store is the blind to be initialized
// retrieve the session
const session = context.app.$session()
// has the session already been initiated?
if (!session.value.initStoreDone) {
// start a new blind
console.log("nuxtServerInit, initialisation d'une nouvelle session")
// put the blind in the session
session.value.store = store.state
// the blind is now initialized
session.value.initStoreDone = true
} else {
console.log("nuxtServerInit, reprise d'un store existant")
// update the store with the session store
store.commit('replace', session.value.store)
}
// save the session
session.save(context)
// log
console.log('initStore terminé, store=', store.state)
}
Os dados armazenados no store são os seguintes:
- linha 6: [jsonSessionStarted] será definido como true assim que uma sessão JSON com o servidor PHP for inicializada com sucesso, quer tenha sido iniciada pelo cliente ou pelo servidor [nuxt]. Após a conclusão desta inicialização, o cookie de sessão para o servidor PHP será recuperado e armazenado na propriedade [phpSessionCookie], linha 10;
- linha 8: [userAuthenticated] será definido como true assim que a autenticação com o servidor PHP for bem-sucedida, quer seja realizada pelo cliente ou pelo servidor [nuxt];
- linha 12: [adminData] será o valor [adminData] obtido do servidor PHP assim que a autenticação for bem-sucedida;
- linhas 18–22: a operação [replace] inicializa as propriedades anteriores com as de um objeto passado como parâmetro;
- linhas 24–26: a mutação [reset] restaura as propriedades do store para os seus valores iniciais;
- linhas 31–37: a função [nuxtServerInit] delega o seu trabalho à função [initStore];
- linhas 39–60: a função [initStore] tem duas funções:
- se o store não tiver sido inicializado, é inicializado e adicionado à sessão;
- se o store já tiver sido inicializado, o seu valor é recuperado da sessão [nuxt];
- linha 42: a sessão nuxt é recuperada;
- linha 44: verificamos se o store foi inicializado:
- se não, o store inicial é colocado na sessão (linha 48);
- depois, na linha 50, indicamos que o store foi inicializado;
- linhas 51–55: se o store foi inicializado, usamo-lo, 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 barramento de eventos acessível ao cliente [nuxt] através de uma função [$eventBus] injetada no contexto do cliente [nuxt]. Não há necessidade de injetá-lo no contexto do servidor [nuxt], pois o servidor não consegue lidar com eventos. No entanto, já vimos que injetá-lo no lado do servidor e depois usá-lo não causa um erro.
/* eslint-disable no-console */
// on crée un bus d'événements entre les vues
import Vue from 'vue'
export default (context, inject) => {
// le bus d'événements
const eventBus = new Vue()
// injection d'une fonction [$eventBus] dans le contexte
inject('eventBus', () => eventBus)
// log
console.log('[fonction $eventBus créée]')
}
Já nos deparámos com este plugin na secção de links. A função [$eventBus] estará disponível para o cliente através da seguinte notação:
- [context.app.$eventBus()] onde 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 mesmo dos exemplos anteriores:
<!-- view layout -->
<template>
<!-- line -->
<div>
<b-row>
<!-- three-column zone -->
<b-col v-if="left" cols="3">
<slot name="left" />
</b-col>
<!-- nine-column zone -->
<b-col v-if="right" cols="9">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// paramètres
props: {
left: {
type: Boolean
},
right: {
type: Boolean
}
}
}
</script>
O componente [navegação] é o seguinte:
<template>
<!-- bootstrap menu with three options -->
<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. [padrão]
O layout [padrão] é aquele utilizado no exemplo [nuxt-11] no parágrafo com o link:
<template>
<div class="container">
<b-card>
<!-- un message -->
<b-alert show variant="success" align="center">
<h4>[nuxt-12] : requêtes HTTP avec axios</h4>
</b-alert>
<!-- la vue courante du routage -->
<nuxt />
<!-- message d’attente -->
<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>
<!-- erreur d’une opération asynchrone -->
<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
}
},
// life cycle
beforeCreate() {
console.log('[default beforeCreate]')
},
created() {
console.log('[default created]')
if (process.client) {
// listen to the evt [loading]
this.$eventBus().$on('loading', this.mShowLoading)
// and event [errorLoadingMessage]
this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
}
},
beforeMount() {
console.log('[default beforeMount]')
},
mounted() {
console.log('[default mounted]')
},
methods: {
// message waiting management
mShowLoading(value) {
console.log('[default mShowLoading], showLoading=', value)
this.showLoading = value
},
// asynchronous operation error
mShowErrorLoading(value, errorLoadingMessage) {
console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
this.showErrorLoading = value
this.errorLoadingMessage = errorLoadingMessage
}
}
}
</script>
- linhas 10–14: exibe a mensagem indicando que o cliente [nuxt] está à espera que uma operação assíncrona seja concluída;
- linhas 15–18: exibem qualquer mensagem de erro proveniente 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] aguarda
- [loading], que sinaliza o início ou o fim de uma espera. A função [mShowLoading] é então executada;
- [errorLoading], que sinaliza que uma mensagem de erro deve ser exibida. A função [mShowErrorLoading] é então executada;
- as páginas [nuxt]:
- exibem a mensagem de carregamento emitindo o evento [‘loading’, true] no barramento de eventos;
- ocultam a mensagem de carregamento emitindo 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 emitindo o evento [‘errorLoading’, false] no barramento de eventos;
15.12.2. [error]
O layout [error] exibe uma mensagem de erro do sistema (não gerida pelo programador):
<!-- définition HTML de la vue -->
<template>
<!-- mise en page -->
<Layout :left="true" :right="true">
<!-- alerte dans la colonne de droite -->
<template slot="right">
<!-- message sur fond rose -->
<b-alert show variant="danger" align="center">
<h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
</b-alert>
</template>
<!-- menu de navigation dans la colonne de gauche -->
<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',
// components used
components: {
Layout,
Navigation
},
// property [props]
props: { error: { type: Object, default: () => 'waiting ...' } },
// life cycle
beforeCreate() {
// client and server
console.log('[error beforeCreate]')
},
created() {
// client and server
console.log('[error created, error=]', this.error)
},
beforeMount() {
// customer only
console.log('[error beforeMount]')
},
mounted() {
// customer only
console.log('[error mounted]')
}
}
</script>
15.13. A página [index] servida pelo servidor [nuxt]

A página [index.vue] é única, na medida em que só é acessível através do servidor [nuxt]. Não é fornecido qualquer link ao utilizador para aceder à mesma através do cliente [nuxt]. O seu código é o seguinte:
<!-- page principale -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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',
// components used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[index asyncData started]')
try {
// start a jSON session
const dao = context.app.$dao()
const response = await dao.initSession()
// log
console.log('[index asyncData response=]', response)
// retrieve session cookie PHP for future requests
const phpSessionCookie = dao.getPhpSessionCookie()
// we store the PHP session cookie in the [nuxt] session
context.store.commit('replace', { phpSessionCookie })
// was there a mistake?
if (response.état !== 700) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// note that the jSON session has started
context.store.commit('replace', { jsonSessionStarted: true })
// we return the result
return { result: '[succès]' }
} catch (e) {
// log
console.log('[index asyncData error=]', e)
// note that session jSON has not started
context.store.commit('replace', { jsonSessionStarted: false })
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// save the blind
const session = context.app.$session()
session.save(context)
// log
console.log('[index asyncData finished]')
}
},
// life cycle
beforeCreate() {
console.log('[index beforeCreate]')
},
created() {
console.log('[index created]')
},
beforeMount() {
console.log('[index beforeMount]')
},
mounted() {
console.log('[index mounted]')
// customer only
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- linha 7: a página exibe o resultado [result] de uma solicitação assíncrona (linhas 46 e 51);
- linha 31: a operação assíncrona consiste em abrir uma sessão JSON com o servidor de cálculo de impostos;
- linha 25: sabemos que, quando a página é solicitada diretamente do servidor [nuxt], a função [asyncData] é executada apenas pelo servidor e não pelo cliente [nuxt], que é executado assim que o navegador recebe a resposta do servidor [nuxt];
- linha 30: recuperamos a camada [dao] do contexto do servidor [nuxt];
- linha 35: se o servidor ainda não tiver feito um 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 (ver o código para a camada [dao] do servidor [nuxt] na secção com link);
- linha 37: este cookie de sessão PHP é armazenado no store;
- linhas 39–42: verificamos se a operação foi bem-sucedida. Caso contrário, é lançada uma exceção, que será capturada pelo [catch] na linha 47;
- linha 44: registamos no armazenamento que a sessão JSON com o servidor PHP foi iniciada;
- linha 46: o resultado [result] é devolvido e apresentado na linha 7;
- linhas 47–54: tratamos quaisquer exceções. Estas podem ser de dois tipos:
- a operação HTTP na linha 31 falhou devido a um erro de comunicação entre o servidor [nuxt] e o servidor PHP;
- a operação HTTP na linha 31 foi bem-sucedida, mas o resultado recebido indicou um erro (linhas 39–42);
- linha 51: observamos que a sessão JSON com o servidor PHP não iniciou;
- linha 53: o resultado [result] é devolvido e apresentado na linha 7. Além disso, as propriedades [showErrorLoading] e [errorLoadingMessage] são definidas, que o cliente [nuxt] 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: recuperamos a sessão [nuxt] do contexto do servidor [nuxt];
- linha 57: guardamo-la;
- 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 a funcionar 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 uma 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] a ter enviado para o cliente. O servidor enviou as informações [result] e, se for o caso, [showErrorLoading] e [errorLoadingMessage].
Sabemos que a função [asyncData] não será executada. Restam, assim, as funções do ciclo de vida, em particular a função [mounted]:
mounted() {
console.log('[index mounted]')
// client seulement
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
- O cliente [nuxt] inclui automaticamente o elemento [result] e, se aplicável, os elementos [showErrorLoading] e [errorLoadingMessage] que lhe são enviados pelo servidor [nuxt] nas propriedades da página:
- a propriedade [result] é exibida na linha 7;
- As propriedades [showErrorLoading, errorLoadingMessage] são utilizadas pelo método [mounted]: na linha 4, a propriedade [showErrorLoading] é verificada. Se for verdadeira, na linha 6, o barramento de eventos [nuxt] do cliente é utilizado para sinalizar que há uma mensagem de erro a ser exibida;
- o evento [errorLoading] acionado na linha 6 é interceptado pela página [layouts/default] descrita na secção com link;
15.15. A página [authentication] executada pelo servidor [nuxt]
A página [authentication] é responsável por autenticar um utilizador junto do servidor de cálculo de impostos. O seu código é o seguinte:
<!-- page d’authentification -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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',
// components used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[authentification asyncData started]')
if (process.client) {
// start waiting for customer [nuxt]
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// authenticate to the server
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// log
console.log('[authentification asyncData response=]', response)
// result
const userAuthenticated = response.état === 200
// we note whether the user is authenticated or not
context.store.commit('replace', { userAuthenticated })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// authentication error?
if (!userAuthenticated) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// we return the result
return { result: '[succès]' }
} catch (e) {
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[authentification asyncData finished]')
if (process.client) {
// end customer waiting [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// customer only
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- linha 7: a página exibe o resultado [result] da solicitação assíncrona [asyncData] das linhas 25–65;
- linhas 28–33: o servidor não executa estas linhas destinadas ao cliente [nuxt];
- linha 36: a camada [dao] é recuperada do servidor [nuxt];
- linha 37: autenticamo-nos no servidor de cálculo de impostos utilizando as credenciais de teste [admin, admin], que são as únicas aceites pelo servidor de cálculo de impostos;
- linha 41: a operação de autenticação só é bem-sucedida se o status da resposta for 200;
- linha 43: definimos a propriedade [userAuthenticated] no store;
- linhas 44–46: o store é guardado na sessão [nuxt];
- linhas 48–51: se a autenticação falhar, lançamos uma exceção com a mensagem de erro enviada pelo servidor de cálculo de impostos;
- caso contrário, na linha 53, devolvemos um resultado de sucesso que será exibido na linha 7;
- linhas 54–57: em caso de erro, são definidas três propriedades da página [result, showErrorLoading, errorLoadingMessage]. A propriedade [result] será exibida na linha 7. As três propriedades serão enviadas para o cliente [nuxt];
- linhas 60–63: não são executadas pelo servidor [nuxt];
- assim que [asyncData] retorna o seu resultado, este é exibido na linha 7. Em seguida, os métodos [beforeCreate] (linhas 67–69) e [created] (linhas 70–72) são executados;
- é isso;
Nota: A página [autentificação] pode não ser executada no servidor [nuxt], por exemplo, se a sessão JSON com o servidor de cálculo de impostos não tiver sido inicializada. Isto pode ser feito da seguinte forma:
- elimine o cookie de sessão PHP do seu navegador (para começar do zero):

- inicie a aplicação [nuxt] enquanto o servidor de cálculo ainda não tiver sido iniciado: irá receber um erro;
- inicie o servidor de cálculo de impostos;
- introduza o URL [/authentication] diretamente na barra de endereços do navegador:

Neste caso, a única solução é recarregar a página [index] novamente.
15.16. A página [authentication] executada pelo cliente [nuxt]
Vamos dar uma olhada no código da página novamente:
<!-- page d’authentification -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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',
// components used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[authentification asyncData started]')
if (process.client) {
// start waiting for customer [nuxt]
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// authenticate to the server
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// log
console.log('[authentification asyncData response=]', response)
// result
const userAuthenticated = response.état === 200
// we note whether the user is authenticated or not
context.store.commit('replace', { userAuthenticated })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// authentication error?
if (!userAuthenticated) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// we return the result
return { result: '[succès]' }
} catch (e) {
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[authentification asyncData finished]')
if (process.client) {
// end customer waiting [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// customer only
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
Existem dois cenários em que a página [authentication] é executada pelo cliente [nuxt]:
- o cliente [nuxt] é executado depois de o servidor [nuxt] ter enviado a página [autenticação] para o navegador do cliente [nuxt];
- o cliente [nuxt] porque o utilizador clicou no link [Autenticação] no menu de navegação:

Vamos primeiro examinar o primeiro cenário. Neste caso, o cliente [nuxt] não executa a função [asyncData]. Ele incorpora o elemento [result] e, se aplicável, os elementos [showErrorLoading] e [errorLoadingMessage] enviados pelo servidor [nuxt] nas propriedades da página:
- a propriedade [result] é exibida na linha 7;
- as propriedades [showErrorLoading, errorLoadingMessage] são utilizadas pelo método [mounted]: na linha 79, a propriedade [showErrorLoading] é verificada. Se for verdadeira, na linha 81, o barramento de eventos do cliente [nuxt] é utilizado para sinalizar que há uma mensagem de erro a ser exibida;
O mecanismo para exibir a mensagem de erro foi explicado para a página [index] na secção de links.
O Caso 2 ocorre quando o cliente [nuxt] é executado após o utilizador clicar no link [Authentication]. Neste caso, o cliente [nuxt] é executado de forma independente 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 que a mensagem de carregamento seja exibida e que qualquer mensagem de erro exibida anteriormente seja apagada;
- linha 36: agora é a camada [dao] do cliente [nuxt] que é recuperada aqui;
- linhas 60–63: o cliente [nuxt] solicita que a mensagem de carregamento seja removida;
- Assim que [asyncData] terminar, o ciclo de vida da página prosseguirá. A função [mounted] nas linhas 76–83 será executada. Se ocorrer 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 da secção de links, mas em vez de solicitar a página [authentication] digitando o seu URL na barra de endereços, utilize o link [Authentication] no menu de navegação. Isto fará com que o cliente [nuxt] seja executado.
15.17. A página [get-admindata]
O código para a página [get-admindata] é o seguinte:
<!-- vue get-admindata -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message -->
<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',
// components used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[get-admindata asyncData started]')
if (process.client) {
// start waiting
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// the data [admindata] is requested
const response = await context.app.$dao().getAdminData()
// log
console.log('[get-admindata asyncData response=]', response)
// result
const adminData = response.état === 1000 ? response.réponse : ''
// put the data in the store
context.store.commit('replace', { adminData })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// was there a mistake?
if (!adminData) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// return the value received
return { result: adminData }
} catch (e) {
// we report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[get-admindata asyncData finished]')
if (process.client) {
// end waiting
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[get-admindata beforeCreate]')
},
created() {
console.log('[get-admindata created]')
},
beforeMount() {
console.log('[get-admindata beforeMount]')
},
mounted() {
console.log('[get-admindata mounted]')
// customer
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 [authentication]. As explicações são as mesmas, quer seja executada pelo servidor [nuxt] ou 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 de impostos (linha 52):

O resultado acima é obtido utilizando tanto o servidor como o cliente [nuxt]. Para provocar um erro, solicite a página [get-admindata] — seja 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:
<!-- page principale -->
<template>
<Layout :left="true" :right="true">
<!-- navigation -->
<Navigation slot="left" />
<!-- message-->
<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',
// components used
components: {
Layout,
Navigation
},
// asynchronous data
async asyncData(context) {
// log
console.log('[fin-session asyncData started]')
// customer case [nuxt]
if (process.client) {
// start waiting
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// a new session PHP is requested from the tax calculation server
const dao = context.app.$dao()
const response = await dao.finSession()
// log
console.log('[fin-session asyncData response=]', response)
// was there a mistake?
if (response.état !== 400) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// the server has sent a new session cookie PHP
// we retrieve it for both the server and the nuxt client
// if this code is executed by the [nuxt] client, the PHP session cookie must be set in the nuxt session
// so that the [plgDao] plugin on the [nuxt] server can retrieve it and initialize the [dao] layer with
// if this code is executed by the [nuxt] server, the PHP session cookie must be set in the nuxt session
// so that the client routing [nuxt] can retrieve it and pass it to the browser
const phpSessionCookie = dao.getPhpSessionCookie()
// we note in the store that the session jSON has been started and we store the session cookie PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// we return the result
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// log
console.log('[fin-session asyncData error=]', e)
// report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[fin-session asyncData finished]')
if (process.client) {
// end waiting
context.app.$eventBus().$emit('loading', false)
}
}
},
// life cycle
beforeCreate() {
console.log('[fin-session beforeCreate]')
},
created() {
console.log('[fin-session created]')
},
beforeMount() {
console.log('[fin-session beforeMount]')
},
mounted() {
console.log('[fin-session mounted]')
// customer only
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. Há apenas um ponto a ter em conta: a operação assíncrona na linha 38 faz com que o servidor de cálculo de impostos envie um novo cookie de sessão PHP. As instruções para lidar com este cookie diferem consoante seja o servidor ou o cliente [nuxt] a executar este código.
Comecemos pelo servidor [nuxt]:
- linha 37: esta é a camada [dao] do servidor [nuxt] que é instanciada. Vamos relembrar o código do seu construtor:
// constructeur
constructor(axios, phpSessionCookie) {
// bibliothèque axios
this.axios = axios
// valeur du cookie de session
this.phpSessionCookie = phpSessionCookie
// nom du cookie de session du serveur PHP
this.phpSessionCookieName = 'PHPSESSID'
}
Na linha 1, vemos que o construtor necessita do cookie de sessão PHP atual — o último recebido, seja do servidor ou do cliente [nuxt];
- linha 52: o servidor [nuxt] recupera o cookie para a nova sessão PHP ou o cookie antigo, caso a operação de encerramento da sessão tenha falhado;
- linha 54: o cookie da sessão PHP é colocado no armazenamento e, em seguida, guardado na sessão [nuxt] nas linhas 56–57;
- após o servidor, é o cliente [nuxt] que executa a página [end-session] com os dados enviados pelo servidor. Sabemos 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 a comunicação com o servidor de cálculo de impostos se encontra na sessão [nuxt];
O facto de o cookie PHP estar na sessão [nuxt] é suficiente para o servidor, porque é aí que a sua camada [dao] o irá recuperar. No plugin [server/plgDao] que inicializa a camada [dao] do servidor, escrevemos:
/* eslint-disable no-console */
// on crée un point d'accès à la couche [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// configuration axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// on récupère le cookie de session
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instanciation de la couche [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// injection d'une fonction [$dao] dans le contexte
inject('dao', () => dao)
// log
console.log('[fonction server $dao créée]')
}
- Linha 13: a camada [dao] do servidor [nuxt] é instanciada com o cookie de sessão PHP obtido da sessão [nuxt], linhas 9–10;
No caso do cliente [nuxt], a situação é diferente. Não é o cliente 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 recebida pelo servidor [nuxt]. Se utilizarmos os links do menu de navegação [3]:

O servidor de cálculo de impostos receberá um cookie de sessão PHP desatualizado do navegador e responderá que não há nenhuma sessão JSON associada a este cookie. Precisamos de encontrar uma forma de passar o novo cookie de sessão PHP para o navegador.
Podemos usar middleware de roteamento para fazer isso:

O script [client/routing] é o middleware de roteamento declarado no ficheiro [nuxt.config]:
// router
router: {
// application URL root
base: '/nuxt-12/',
// routing middleware
middleware: ['routing']
},
O script [middleware/routing] é o seguinte:
/* eslint-disable no-console */
// on importe le middleware du client
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.client) {
// routage client
clientRouting(context)
}
}
- Linhas 9–12: Apenas encaminhamos o cliente utilizando uma função importada na linha 4;
O script [middleware/client/routing] é o seguinte:
/* 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
}
}
Voltemos à situação imediatamente após a página [fin-session] ser executada pelo servidor [nuxt]:

Se clicar num dos links do menu [3], o cliente [nuxt] assume o controlo. Como haverá uma mudança de página, o script de roteamento 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, é enviado para o navegador (linha 16). A partir deste ponto, o navegador do cliente [nuxt] tem o cookie de sessão PHP correto;
O script [client/routing] é executado sempre que o cliente [nuxt] muda de página. O código do script funciona independentemente da página de destino: essencialmente, na maioria das vezes, envia ao navegador um cookie de sessão PHP que este já possui, exceto em dois casos:
- imediatamente após o início 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 [end-session], tal como acabado de explicar;
Agora, vamos examinar o caso em que a página [end-session] é executada apenas pelo cliente [nuxt], porque o utilizador clicou no seu link no menu de navegação. Agora é o cliente [nuxt] que executa a função [asyncData]:
try {
// a new session PHP is requested from the tax calculation server
const dao = context.app.$dao()
const response = await dao.finSession()
// log
console.log('[fin-session asyncData response=]', response)
// was there a mistake?
if (response.état !== 400) {
// the error is in response.réponse
throw new Error(response.réponse)
}
// the server has sent a new session cookie PHP
// we retrieve it for both the server and the nuxt client
// if this code is executed by the [nuxt] client, the PHP session cookie must be set in the nuxt session
// so that the [plgDao] plugin on the [nuxt] server can retrieve it and initialize the [dao] layer with
// if this code is executed by the [nuxt] server, the PHP session cookie must be set in the nuxt session
// so that the client routing [nuxt] can retrieve it and pass it to the browser
const phpSessionCookie = dao.getPhpSessionCookie()
// we note in the store that the session jSON has been started and we store the session cookie PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// save the store in the [nuxt] session
const session = context.app.$session()
session.save(context)
// we return the result
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// log
console.log('[fin-session asyncData error=]', e)
// report the error
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[fin-session asyncData finished]')
if (process.client) {
// end waiting
context.app.$eventBus().$emit('loading', false)
}
}
- Linha 3: Esta é a camada [dao] do cliente [nuxt] que é recuperada aqui;
- Linha 18: O cookie de sessão PHP recuperado pela camada [dao] do cliente [nuxt] é armazenado, colocado no store (linha 20) e, em seguida, guardado na sessão [nuxt] (linhas 22–23);
- A partir daí, tudo funciona bem porque sabemos que a camada [dao] do servidor [nuxt] irá recuperar o cookie de sessão PHP da sessão [nuxt];
15.19. Execução
Para executar este exemplo, deve primeiro eliminar o cookie de sessão [nuxt] e o cookie PHP do navegador que está a executar o cliente [nuxt] para começar do zero. Segue-se um exemplo utilizando o navegador Chrome:

15.20. Conclusão
Este exemplo foi particularmente complexo. Reuniu conhecimentos adquiridos em exemplos anteriores: persistência de armazenamento numa sessão [nuxt], plugins de injeção de funções, middleware de roteamento e tratamento de erros para operações assíncronas. A complexidade foi aumentada pelo facto de querermos que o utilizador pudesse utilizar tanto os links do menu de navegação como digitar manualmente URLs sem interromper o funcionamento da aplicação. Para conseguir isto, tivemos de examinar como cada página se comportava, dependendo se era executada pelo cliente ou pelo servidor [nuxt].
Esta consistência no comportamento do cliente e do servidor [Nuxt] não é essencial. Considere o cenário comum em que:
- a primeira página é servida pelo servidor [nuxt];
- todas as páginas subsequentes são servidas pelo cliente [nuxt], que passa então a operar no modo [SPA];
No entanto, mesmo neste caso, deve verificar como todas as páginas são renderizadas quando executadas pelo servidor [nuxt], pois é isso que os motores de busca verão quando as solicitarem.