Skip to content

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 fazer solicitações HTTP com a biblioteca [axios]. Além disso, vamos utilizar conceitos já aprendidos:

  • o uso de plug-ins do exemplo [nuxt-06]:
  • a persistência do store em um cookie de sessão do exemplo [nuxt-06];
  • o controle da navegação com middlewares do exemplo [nuxt-09];
  • o gerenciamento de erros do exemplo [nuxt-11];

A arquitetura do exemplo será a seguinte:

Image

  • a aplicação [nuxt] será armazenada no servidor [node.js] [3], baixada pelo navegador [1], que a executará em seguida;
  • tanto o cliente [nuxt] [1] quanto o servidor [nuxt] [3] farão solicitações HTTP ao servidor de dados [2]. Esse servidor será o servidor de cálculo de impostos desenvolvido na seção PHP 7. Utilizaremos 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:

Image

  • em [1], o servidor [node.js] fornece as páginas [nuxt] ao navegador [2]. É a camada [web] [8] do servidor que fornece essas páginas. Para entregar a página, o servidor pode ter solicitado dados externos ao servidor de dados [3]. É a camada [DAO] [9] que realiza 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 então será executada no modo SPA. O bloco [UI] (Interface do Usuário) [4] apresenta as páginas [vue.js] ao usuário. As ações do usuário 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, realiza as consultas HTTP necessárias;

15.2. Estrutura do projeto

Image

15.3. O arquivo de configuração [nuxt.config.js]

O projeto será controlado pelo arquivo [nuxt.config.js] a seguir:


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) { }
  },
  // diretório do código-fonte
  srcDir: 'nuxt-12',
  // roteador
  router: {
    // raiz dos URL do aplicativo
    base: '/nuxt-12/',
    // middleware de roteamento
    middleware: ['routing']
  },
  // servidor
  server: {
    // porta de serviço, 3000 por padrão
    port: 81,
    // endereços de rede monitorados, por padrã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/version-14',
    // configuração do cookie de sessão [nuxt]
    maxAge: 60 * 5
  }
}
  • linha 22: nós mesmos gerenciamos o alerta de espera pelo término de uma ação assíncrona;
  • linha 31: utilizaremos diversos plug-ins que serão especializados para o cliente ou para o servidor, mas não para ambos ao mesmo tempo;
  • linha 52: o módulo [axios] está integrado ao [nuxt]. Isso fará com que o objeto [axios], que fará as solicitações HTTP doaplicativo [nuxt] para o servidor PHP de cálculo de impostos, estará disponível no [context.$axios];
  • linha 54: o módulo [cookie-universal-nuxt] nos permitirá salvar a sessão [nuxt] em um cookie;
  • linha 60: a propriedade [axios] nos permite configurar o módulo [@nuxtjs/axios] da linha 52. Não utilizaremos essa opção, 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 o uso de cookies nas trocas de dados com o servidor de cálculo de impostos;
  • linha 92: o URL básico do servidor de cálculo de impostos;
  • linha 94: tempo de vida da sessão Nuxt (5 minutos);
  • linha 77: a navegação entre o cliente e o servidor [nuxt] será controlada por um middleware de roteamento;

15.4. A camada [UI] do aplicativo

Image

Vamos conceder ao aplicativo [nuxt] acesso ao API do servidor de cálculo de impostos por meio da seguinte visualização:

Image

  • no [2], o menu que dá acesso ao API do servidor de cálculo de impostos:
    • [Authentification]: corresponde à página [authentification]. Essa página envia uma solicitação de autenticação ao servidor de cálculo de impostos com os identificadores [admin, admin], que, por enquanto, são os únicos autorizados. O resultado exibido é semelhante ao de [3];
    • [Requête AdminData]: corresponde à página [get-admindata]. Essa página solicita ao servidor de cálculo de impostos os dados, aqui denominados [adminData], que permitem o cálculo do imposto. O resultado exibido é semelhante ao de [3];
    • [Fin session impôt]: corresponde à página [fin-session]. Essa página envia uma solicitação de encerramento da sessão PHP ao servidor de cálculo do imposto. O servidor, então, cancela a sessão atual PHP e inicializa uma nova sessão em branco;

15.5. As camadas [dao] do aplicativo [nuxt]

Conforme indicado acima, a arquitetura do aplicativo [nuxt] será a seguinte:

Image

  • em [1], o servidor [node.js] fornece as páginas [nuxt] ao navegador [2]. É a camada [web] [8] do servidor que fornece essas páginas. Para fornecer a página, o servidor pode ter solicitado dados externos ao servidor de dados [3]. É a camada [DAO] [9] que realiza 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 Usuário) [4] apresenta as páginas [vue.js] ao usuário. As ações desse 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, realiza as consultas HTTP necessárias;

Utilizaremos a versão 14 do servidor de cálculo de impostos desenvolvido no documento |Introdução à linguagem PHP7 por meio de exemplos|. Utilizaremos apenas uma parte de sua API (Interface de Programação de Aplicativos) API:

Consulta
Resposta
1
2
3
4
5
6
[initialisation d’une session jSON avec le
serveur de calcul de l’impôt]
[une session PHP est créée avec le serveur de
calcul de l’impôt]

GET main.php?action=init-session&type=json

{
    "action": "init-session",
    "état"700,
    "réponse": "session démarrée avec type [json]"
}

[authentification de l’utilisateur]
[l’authentification est stockée dans la
session PHP]

POST main.php?action=authentifier-utilisateur
paramètres postés : user, admin

{
    "action": "authentifier-utilisateur",
    "état"200,
    "réponse": "Authentification réussie [admin, admin]"
}

[demande des données de l’administration
fiscale]
[les données reçues sont stockées dans la
session PHP]

GET main.php?action=get-admindata

{
    "action": "get-admindata",
    "état"1000,
    "réponse": {
        "limites": [
            9964,
            27519,
            73779,
            156244,
            0
        ],
        "coeffR": [
            0,
            0.14,
            0.3,
            0.41,
            0.45
        ],
        "coeffN": [
            0,
            1394.96,
            5798,
            13913.69,
            20163.45
        ],
        "plafondQfDemiPart": 1551,
        "plafondRevenusCelibatairePourReduction": 21037,
        "plafondRevenusCouplePourReduction": 42074,
        "valeurReducDemiPart": 3797,
        "plafondDecoteCelibataire": 1196,
        "plafondDecoteCouple": 1970,
        "plafondImpotCouplePourDecote": 2627,
        "plafondImpotCelibatairePourDecote": 1595,
        "abattementDixPourcentMax": 12502,
        "abattementDixPourcentMin": 437
    }
}

[fin de la session PHP avec le serveur de
calcul de l’impôt]
[supprime la session PHP courante et crée une
nouvelle session PHP. Dans celle-ci, la
session jSON reste activée, mais l’utilisateur
n’est plus authentifié]

GET main.php?action=fin-session

{
    "action": "fin-session",
    "état"400,
    "réponse": "session supprimée"
}

15.5.1. A camada [dao] do servidor [nuxt]

Image

O servidor [node.js] [1] utilizará a camada [dao] descrita no documento |Introdução à estrutura VUE.JS por meio de um exemplo|. Repetimos aqui 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 consulta 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 da 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 diversas 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 é uma matriz
      // procura-se o cookie de sessão nessa matriz
      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) {
          // armazenamos o cookie de sessão
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // encontramos
          trouvé = true;
        } else {
          // próximo elemento
          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] retornam 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]: status=700 para uma resposta sem erros;
      • [authentifierUtilisateur]: status=200 para uma resposta sem erros;
      • [getAdminData]: status=1000 para uma resposta sem erros;
      • [fin-session]: status=400 para uma resposta sem erros;
    • [réponse]: resposta associada ao indicador numérico [état]. Pode variar de acordo com esse indicador numérico;

Vamos examinar 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 fará as solicitações HTTP;
  • linha 5: o nome do cookie de sessão enviado pelo servidor de dados, gravado 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 caso do cookie de sessão, precisamos considerar duas camadas [dao] distintas:

  • a do navegador;
  • a do servidor;

Teremos que gerenciar três cookies de sessão:

  1. aquele trocado entre o cliente [nuxt] e o servidor PHP 7;
  2. aquele trocado entre o servidor [nuxt] e o servidor PHP 7;
  3. aquele trocado entre o cliente [nuxt] e o servidor [nuxt];

Faremos com que o cookie de sessão com o servidor PHP seja o mesmo para o cliente e para o servidor [nuxt]. Chamaremos esse cookie de “cookie de sessão PHP”. Esse cookie é o dos casos 1 e 2. Chamaremos de “cookie de sessão [nuxt]” o cookie do caso 3. Portanto, teremos 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 usar o mesmo cookie para as sessões PHP do cliente e do navegador [nuxt]? Queremos que o aplicativo possa se comunicar com o servidor PHP, independentemente de ser com o cliente ou com o servidor [nuxt]:

  • se uma ação A do servidor [nuxt] colocar o servidor PHP em um estado E, esse estado será 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 seguisse à ação A do servidor [nuxt] encontraria o servidor PHP noestado E deixado pelo servidor [nuxt] e, portanto, poderia aproveitar o trabalho já realizado pelo servidor [nuxt];
  • se, após a ação B do cliente [nuxt], ocorrer uma ação C do servidor [nuxt], pela mesma razão que anteriormente, essa ação poderá se basear no trabalho realizado pela ação B do cliente [nuxt];

Para que o navegador do cliente [nuxt] possa se comunicar com o servidor PHP de cálculo de impostos, utilizaremos a versão 14 desse servidor, que permite chamadas entre domínios, ou seja, aquelas 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. Esse conceito existe apenas para chamadas feitas 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;

O gerenciamento do cookie de sessão PHP acima não é adequado para o servidor [nuxt]: sua camada [dao] é instanciada a cada nova solicitação feita ao servidor [nuxt]. Lembramos, de fato, que solicitar uma página ao servidor [nuxt] equivale a reinicializar o aplicativo [nuxt]. Assim, quando, ao 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 HTTP seguinte do mesmo servidor [nuxt], pois, nesse intervalo, sua camada [dao] foi recriada, o construtor foi reexecutado e o cookie de sessão PHP foi reinicializado com a string vazia (linha 6);

Uma solução é 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 o servidor [nuxt] poderá fornecer esse cookie de sessão PHP ao construtor de sua camada [dao]? Armazenaremos o cookie de sessão PHP no cookie de sessão [nuxt] trocado entre o navegador e o servidor [nuxt]. O processo é o seguinte:

  1. a aplicação [nuxt] é iniciada;
  2. quando o servidor [nuxt] faz sua primeira solicitação HTTP ao servidor PHP, ele armazena o cookie de sessão PHP que recebeu no cookie de sessão [nuxt] que troca com o cliente [nuxt];
  3. o navegador que hospeda o cliente [nuxt] recebe esse cookie de sessão [nuxt] e, portanto, o reenvia sistematicamente a cada nova solicitação ao servidor [nuxt];
  4. quando o servidor [nuxt] precisar fazer uma nova solicitação ao servidor PHP, ele encontrará o cookie de sessão PHP no cookie de sessão [nuxt] que o navegador lhe enviou. Em seguida, ele o enviará ao servidor PHP;

Existem, de fato, dois cookies de sessão, e não se deve confundi-los:

  • 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]. Ele 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 diversas 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 é uma matriz
      // procura-se o cookie de sessão nessa matriz
      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) {
          // armazenamos o cookie de sessão
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // encontrado
          trouvé = true;
        } else {
          // próximo elemento
          i++;
        }
      }
    }
    // a resposta do servidor está em [response.data]
    return response.data;
  }
  • linhas 30-43: procura-se o cookie [PHPSESSID=xxx]. Se for encontrado, ele é armazenado na classe (linha 36);

Esse 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]. Aqui está um exemplo obtido com um cliente [Postman]:

Image

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

Image

  • em [5], o servidor PHP indica, em primeiro lugar, que excluiu a sessão atual PHP;
  • 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, na verdade, o cookie que deve ser memorizado é o [6].

Portanto, é necessário atualizar 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 {
      // 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 diversas 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 são uma matriz
      // procura-se o cookie de sessão PHP nessa matriz
      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) {
          // o cookie de sessão PHP é armazenado
          const phpSessionCookie = results[1]
          // existe a palavra [deleted] nessa tabela?
          const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
          if (!results2) {
            // temos o cookie de sessão correto PHP
            this.phpSessionCookie = phpSessionCookie
            // encontramos
            trouvé = true
          } else {
            // o elemento seguinte
            i++
          }
        } else {
          // próximo elemento
          i++
        }
      }
    }
    // a resposta do servidor está em [response.data]
    return response.data
  }
  • linha 41: foi encontrado um cookie com o nome [PHPSESSID]. Ele é armazenado localmente;
  • linha 43: verificamos se, no cookie salvo, há a sequência [PHPSESSID=deleted];
  • linha 46: se a resposta for não, significa que encontramos o cookie correto, [PHPSESSID]. Ele é armazenado na classe;

Após a função [getRemoteData], o cookie de sessão PHP é armazenado na classe, em [this.phpSessionCookie]. Dissemos 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 isso, adicionamos um novo método a ela:


// 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 haja um;
  • uma vez realizada a ação, o servidor [nuxt] recupera o cookie de sessão PHP armazenado pela camada [dao] por meio do método [getPhpSessionCookie] anterior. Esse 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 havia nenhum cookie de sessão PHP anteriormente);
    • durante a execução do método [finSession] (o servidor PHP altera o cookie de sessão PHP);

Vale destacar uma particularidade do cookie de sessão PHP. O servidor [nuxt] nem sempre recebe esse cookie do servidor PHP. Na verdade, este último o envia apenas uma vez. Depois disso, ele não o envia mais. Ao analisar o código de [getRemoteData] e o de [getPhpSessionCookie], percebe-se que, quando o servidor PHP não envia um cookie de sessão, a função [getPhpSessionCookie] retorna o cookie de sessão PHP fornecido ao construtor. É assim que o servidor sempre envia ao servidor PHP o último cookie de sessão PHP que este lhe enviou.

15.5.2. A camada [dao] do cliente [nuxt]

Image

Para o cliente [nuxt], que é executado em um navegador, retomamos o código da classe [Dao] do documento |Introdução à estrutura VUE.JS por meio 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 consulta 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 {
      // 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 diversas 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;

Esse código difere da camada [dao] do servidor [nuxt] pelo fato de não gerenciar o cookie de sessão PHP com o servidor de cálculo de impostos: é o navegador que faz isso.

Vamos, assim como fizemos para a camada [dao] do servidor [nuxt], 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 da consulta URL
      params: {
        action: 'fin-session'
      }
    }
    // execução da consulta HTTP
    const data = await this.getRemoteData(options)
    // resultado
    return data
  }

Quando o cliente [nuxt] executa esse método, ele recebe, assim como o servidor [nuxt], dois cookies de sessão PHP. Na verdade, é o navegador que os recebe e lida corretamente com a situação: ele mantém apenas o cookie da nova sessão PHP, iniciada pelo servidor de cálculo de impostos. Portanto, na próxima ação do cliente [nuxt] em direção ao servidor PHP, o cookie de sessão PHP estará correto, pois é 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. Em suas comunicações com o servidor PHP, ele enviará, então, um cookie de sessão PHP que já não existe, e isso causará problemas. O cliente [nuxt] deveria avisar o servidor [nuxt] de que o cookie de sessão PHP mudou e transmitir esse novo cookie a ele. Sabemos como ele pode fazer isso: por meio do cookie de sessão [nuxt], o cookie trocado entre o cliente e o servidor [nuxt]. O cliente [nuxt] tem pelo menos duas maneiras de recuperar o novo cookie de sessão PHP:

  1. solicitando-o ao navegador;
  2. usando o método [getRemoteData] do servidor, que sabe como recuperar o novo cookie de sessão PHP;

Vamos usar a segunda solução, pois ela já está pronta. O método [getRemoteData] do cliente [nuxt] passa a ser o seguinte:


async getRemoteData(options) {
    // 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 diversas 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 são uma matriz
      // procura-se o cookie de sessão PHP nessa matriz
      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) {
          // o cookie de sessão PHP é armazenado
          const phpSessionCookie = results[1]
          // existe a palavra [deleted] nessa tabela?
          const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
          if (!results2) {
            // temos o cookie de sessão correto PHP
            this.phpSessionCookie = phpSessionCookie
            // encontramos
            trouvé = true
          } else {
            // o elemento seguinte
            i++
          }
        } else {
          // próximo elemento
          i++
        }
      }
    }
    // a resposta do servidor está em [response.data]
    return response.data
  }

Mantivemos em [getRemoteData] apenas o código que processa a resposta do servidor PHP na busca pelo 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, pois essa tarefa fica a cargo do navegador que hospeda o cliente [nuxt].

Assim que o cookie de sessão PHP for obtido pelo cliente [nuxt], ele deve ser inserido na sessão [nuxt] para que o servidor [nuxt] possa utilizá-lo. Não é a camada [dao] que se encarrega disso, mas ela fornece acesso, por meio de um método, ao cookie de sessão PHP que armazenou:


// acesso ao cookie da sessão PHP
  getPhpSessionCookie() {
    return this.phpSessionCookie
}

A função [getPhpSessionCookie] nem sempre retorna um cookie de sessão válido:

  • é preciso lembrar aqui que a camada [dao] do cliente [nuxt] é persistente. Ela é instanciada uma vez e permanece 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] retorna 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é que seja substituído por um novo cookie de sessão PHP enviado pelo servidor PHP. A função [getPhpSessionCookie] do cliente [nuxt] retorna, então, o último cookie de sessão PHP recebido;

A camada [dao] do cliente [nuxt] difere da do servidor [nuxt] apenas em um ponto: ela não envia o cookie de sessão PHP por conta própria, pois é o navegador que o faz. No entanto, optou-se por manter duas camadas [dao] distintas, pois os raciocínios que levam às respectivas implementações são diferentes.

15.6. A sessão [nuxt]

Image

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 em um cookie
  save(context) {
    // armazenamento do store na sessão
    this.value.store = context.store.state
    console.log('nuxt-session save=', this.value)
    // salvamento 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')
    // salvamento do novo store na sessão e salvamento da sessão
    this.save(context)
  }
}
// exportação da sessão
export default session
  • linhas 5-10: a sessão possui apenas uma propriedade [value] com duas subpropriedades:
    • [initStoreDone], que indica se o store foi inicializado ou não;
    • [store]: o valor [store.state] do store Vuex do aplicativo;
  • linhas 12-18: o método [save] serve para salvar a sessão [nuxt] em um cookie. Aqui, utiliza-se a biblioteca [cookie-universal-nuxt] para gerenciar o cookie. Observe-se 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, salvo na sessão, linha 25;

15.7. Os plug-ins de gerenciamento da sessão [nuxt]

Image

15.7.1. O plugin de gerenciamento de sessão [nuxt] do serveur [nuxt]

Ao iniciar o aplicativo, o servidor [nuxt] é o primeiro a entrar em operação. Portanto, é ele que 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) => {
  // gerenciamento da sessão no servidor
  console.log('[plugin server plgSession]')

  // existe uma 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, armazenamos seu valor na sessão importada na linha 4;
  • linha 22: a sessão foi inicializada ou restaurada. Ela é disponibilizada por meio da função [$session];

15.7.2. O plug-in de gerenciamento de 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) => {
  // gerenciamento 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 atual [nuxt] no cookie [nuxt-session];
  • linha 13: a sessão [nuxt], importada na linha 4, é devolvida por meio da função injetada [$session];

15.8. Os plug-ins das camadas [dao]

Image

15.8.1. O plug-in 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)
  // registro
  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 realizará as consultas HTTP da camada [dao] do cliente [nuxt] com as informações do arquivo [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 10: a camada [dao] do cliente [nuxt] é instanciada;
  • linha 12: a função [$dao] é injetada no contexto e nas páginas do cliente. Essa função dá acesso à camada [dao] da linha 10;

Portanto, deve-se lembrar que, para ter acesso à camada [dao] do cliente [nuxt] quando este estiver sendo executado, deve-se escrever:

  • [context.app.$dao()], quando o contexto for conhecido;
  • [this.$dao()] em uma página [Vue.js];

15.8.2. O plug-in 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)
  // registro
  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 realizará as consultas HTTP da camada [dao] do servidor [nuxt] com as informações do arquivo [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 ele é 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]. Essa função dá acesso à camada [dao] da linha 13;

Portanto, deve-se lembrar que, para ter acesso à camada [dao] do servidor [nuxt] quando este estiver em execução, deve-se escrever:

  • [context.app.$dao()], quando o contexto for conhecido;
  • [this.$dao()] em uma página [Vue.js];

15.9. O store Vuex

Image

O store [Vuex] armazenará todos os dados que devem ser compartilhados pelos diferentes componentes do aplicativo [pages, client, serveur], sem que esses dados sejam, necessariamente, reativos.


/* eslint-disable no-console */

// estado da persiana
export const state = () => ({
  // sessão jSON iniciada
  jsonSessionStarted: false,
  // usuário autenticado
  userAuthenticated: false,
  // cookie de sessão PHP
  phpSessionCookie: '',
  // adminData
  adminData: ''
})

// alterações no store
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 esse código?
    console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
    // inicializar sessão
    initStore(store, context)
  }
}

function initStore(store, context) {
  // store é a sessão a ser inicializada
  // recuperamos 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")
    // colocamos o store na sessão
    session.value.store = store.state
    // o store agora está inicializado
    session.value.initStoreDone = true
  } else {
    console.log("nuxtServerInit, reprise d'un store existant")
    // atualizando o armazenamento com o armazenamento da sessão
    store.commit('replace', session.value.store)
  }
  // a sessão é salva
  session.save(context)
  // registro
  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, seja ela realizada pelo cliente ou pelo servidor [nuxt]. Ao final dessa 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 no servidor PHP for bem-sucedida, independentemente de ter sido realizada pelo cliente ou pelo servidor [nuxt];
  • linha 12: [adminData] será o valor [adminData] obtido 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] restabelece os valores iniciais das propriedades do store;
  • linhas 31-37: a função [nuxtServerInit] delega sua tarefa à função [initStore];
  • linhas 39-60: a função [initStore] tem duas funções:
    • se o store não tiver sido inicializado, ele é inicializado e colocado na sessão;
    • se o store já tiver sido inicializado, 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, o store inicial é colocado na sessão (linha 48);
    • em seguida, na linha 50, indica-se que o store foi inicializado;
  • linhas 51-55: se o store estiver inicializado, ele é utilizado, na linha 54, para inicializar o store com o valor contido na sessão;
  • linha 57: em todos os casos, a sessão é salva no cookie [nuxt-session], juntamente com o store que ela contém;

15.10. O plugin [plgEventBus]

Image

Este plugin tem como objetivo tornar um canal de eventos acessível ao cliente [nuxt] por meio de uma função [$eventBus] injetada no contexto do cliente [nuxt]. Não faz sentido injetá-lo no contexto do servidor [nuxt], pois este não sabe lidar com eventos. No entanto, já vimos que injetá-lo no lado do servidor e depois utilizá-lo não causa erros.


/* eslint-disable no-console */
// cria-se um bus de eventos entre as visualizações
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)
  // registro
  console.log('[fonction $eventBus créée]')
}

Já nos deparamos com esse plugin no parágrafo sobre links. A função [$eventBus] estará disponível no cliente por meio das anotações:

  • [context.app.$eventBus()], onde o contexto estiver disponível;
  • [this.$eventBus()] nas páginas [Vue.js] do cliente;

15.11. Os componentes do aplicativo [nuxt]

Image

O componente [layout] é o dos exemplos anteriores:


<!-- disposição das visualizações -->
<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 do aplicativo [nuxt]

Image

15.12.1. [default]

O layout [default] é o utilizado para o exemplo [nuxt-11] no parágrafo “link”:


<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 visualização atual do roteamento -->
      <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 em uma 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) {
      // estamos escutando o evento [loading]
      this.$eventBus().$on('loading', this.mShowLoading)
      // assim como o evento [errorLoadingMessage]
      this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
    }
  },
  beforeMount() {
    console.log('[default beforeMount]')
  },
  mounted() {
    console.log('[default mounted]')
  },
  methods: {
    // gerenciamento da mensagem em espera
    mShowLoading(value) {
      console.log('[default mShowLoading], showLoading=', value)
      this.showLoading = value
    },
    // erro em uma 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: exibem a mensagem de espera pelo término de uma operação assíncrona do cliente [nuxt];
  • linhas 15-18: exibem 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], 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 sinaliza que é necessário exibir 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] exibe uma mensagem de erro do sistema (não gerenciada pelo desenvolvedor):


<!-- definição da visualização HTML -->
<template>
  <!-- formatação -->
  <Layout :left="true" :right="true">
    <!-- alerta na coluna da direita -->
    <template slot="right">
      <!-- mensagem com 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() {
    // somente cliente
    console.log('[error beforeMount]')
  },
  mounted() {
    // somente cliente
    console.log('[error mounted]')
  }
}
</script>

15.13. A página [index] executada pelo servidor [nuxt]

Image

A página [index.vue] tem a particularidade de ser acessível apenas por meio do servidor [nuxt]. Nenhum link é apresentado ao usuário para acessá-la por meio do cliente [nuxt]. 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) {
    // log
    console.log('[index asyncData started]')
    try {
      // iniciando uma sessão jSON
      const dao = context.app.$dao()
      const response = await dao.initSession()
      // log
      console.log('[index asyncData response=]', response)
      // recuperando o cookie de sessão PHP para as próximas solicitações
      const phpSessionCookie = dao.getPhpSessionCookie()
      // o cookie de sessão PHP é armazenado na sessão [nuxt]
      context.store.commit('replace', { phpSessionCookie })
      // houve algum erro?
      if (response.état !== 700) {
        // o erro está em response.réponse
        throw new Error(response.réponse)
      }
      // observa-se que a sessão jSON foi iniciada
      context.store.commit('replace', { jsonSessionStarted: true })
      // retornamos o resultado
      return { result: '[succès]' }
    } catch (e) {
      // registro
      console.log('[index asyncData error=]', e)
      // registra-se que a sessão jSON não foi iniciada
      context.store.commit('replace', { jsonSessionStarted: false })
      // o erro é relatado
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // salva-se o store
      const session = context.app.$session()
      session.save(context)
      // log
      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]')
    // somente cliente
    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 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] é executada apenas 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 uma solicitação ao servidor de cálculo de impostos, ele recebe 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: esse 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: registra-se no store que a sessão jSON com o servidor PHP foi iniciada;
  • linha 46: retorna-se o resultado [result], que é exibido 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: observa-se que a sessão jSON com o servidor PHP não foi iniciada;
  • linha 53: é retornado o resultado [result], que é exibido na linha 7. Além disso, definem-se as propriedades [showErrorLoading] e [errorLoadingMessage], que o cliente [nuxt] utilizará para exibir uma mensagem de erro ao 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: ela é salva;
  • linhas 63-68: assim que a função [asyncData] for concluída, o servidor [nuxt] executa as funções [beforeCreate] e [create];

Observação: 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 no momento em que a aplicação [nuxt] for iniciada:

Image

Nesse 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 que o servidor [nuxt] a envia a ele. Este último enviou a ele 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, em particular, a função [mounted]:


mounted() {
    console.log('[index mounted]')
    // somente 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] é exibida 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 exibida;
  • o evento [errorLoading], disparado na linha 6, é interceptado pela página [layouts/default] descrita no parágrafo “link”;

15.15. A página [authentification] é executada pelo servidor [nuxt]

A página [authentification] é responsável por identificar um usuário junto ao servidor de cálculo de impostos. 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) {
    // log
    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')
      // registro
      console.log('[authentification asyncData response=]', response)
      // resultado
      const userAuthenticated = response.état === 200
      // registra-se se o usuário está autenticado ou não
      context.store.commit('replace', { userAuthenticated })
      // o store é salvo na sessão [nuxt]
      const session = context.app.$session()
      session.save(context)
      // erro de autenticação?
      if (!userAuthenticated) {
        // o erro está em response.réponse
        throw new Error(response.réponse)
      }
      // o resultado é o seguinte
      return { result: '[succès]' }
    } catch (e) {
      // o erro é sinalizado
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // registro
      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]')
    // somente cliente
    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 consulta assíncrona [asyncData] das linhas 25 a 65;
  • linhas 28 a 33: o servidor não executa essas linhas destinadas ao cliente [nuxt];
  • linha 36: recupera-se a camada [dao] do servidor [nuxt];
  • linha 37: realiza-se a autenticação no servidor de cálculo de impostos com os dados de teste [admin, admin], que são os únicos aceitos pelo servidor de cálculo de impostos;
  • linha 41: a operação de autenticação foi bem-sucedida apenas se a resposta for do status 200;
  • linha 43: a propriedade [userAuthenticated] é armazenada no store;
  • linhas 44-46: o store é salvo na sessão [nuxt];
  • linhas 48-51: se a autenticação falhar, lança-se uma exceção com a mensagem de erro enviada pelo servidor de cálculo de impostos;
  • caso contrário, na linha 53, retorna-se um resultado de sucesso que será exibido 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á exibida 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 [asyncData] retornar seu resultado, este será exibido na linha 7. Em seguida, os métodos [beforeCreate] (linhas 67-69) e [created] (linhas 70-72) são executados;
  • pronto;

Observação: 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 de impostos não tiver sido inicializada. Isso pode ser feito da seguinte maneira:

  • exclua o cookie de sessão PHP do seu navegador (para recomeçar do zero):

Image

  • inicie o aplicativo [nuxt] enquanto o servidor de cálculo não estiver em execução: você receberá uma mensagem de erro;
  • inicie o servidor de cálculo do imposto;
  • digite URL [/authentification] diretamente na barra de endereços do navegador:

Image

Nesse caso, a única solução é, mais uma vez, recarregar a página [index].

15.16. A página [authentification] executada pelo cliente [nuxt]

Vamos rever o 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) {
    // log
    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')
      // registro
      console.log('[authentification asyncData response=]', response)
      // resultado
      const userAuthenticated = response.état === 200
      // registra-se se o usuário está autenticado ou não
      context.store.commit('replace', { userAuthenticated })
      // o store é salvo na sessão [nuxt]
      const session = context.app.$session()
      session.save(context)
      // erro de autenticação?
      if (!userAuthenticated) {
        // o erro está em response.réponse
        throw new Error(response.réponse)
      }
      // o resultado é retornado
      return { result: '[succès]' }
    } catch (e) {
      // o erro é sinalizado
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // registro
      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]')
    // somente 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]:

  1. o cliente [nuxt] é executado depois que o servidor [nuxt] enviou ao navegador do cliente [nuxt] a página [authentification];
  2. o cliente [nuxt] é iniciado porque o usuário clicou no link [Authentification] do menu de navegação:

Image

Vamos analisar primeiro o primeiro caso. Nesse caso, o cliente [nuxt] não executa a função [asyncData]. Ele incorpora nas propriedades da página os elementos [result] e, eventualmente, [showErrorLoading, errorLoadingMessage] que lhe foram enviados pelo servidor [nuxt]:

  • a propriedade [result] é exibida pela 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 há uma mensagem de erro a ser exibida;

O mecanismo de exibição da mensagem de erro foi explicado para a página [index] no parágrafo “link”.

O caso 2 é o do cliente [nuxt] executado quando o usuário clica no link [Authentification]. Nesse 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 uma eventual 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 encerramento da exibição da mensagem de espera;
  • assim que [asyncData] for concluída, o ciclo de vida da página será executado. A função [mounted] das linhas 76-83 será executada. Caso tenha ocorrido um erro, a mensagem de erro será 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 acessar a página [authentification] digitando seu endereço URL na barra de endereços, use o link [Authentification] do menu de navegação. Assim, o cliente [nuxt] será 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) {
    // log
    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()
      // registro
      console.log('[get-admindata asyncData response=]', response)
      // resultado
      const adminData = response.état === 1000 ? response.réponse : ''
      // os dados são inseridos no armazenamento
      context.store.commit('replace', { adminData })
      // o armazenamento é salvo na sessão [nuxt]
      const session = context.app.$session()
      session.save(context)
      // houve algum erro?
      if (!adminData) {
        // o erro está em response.réponse
        throw new Error(response.réponse)
      }
      // retornamos o valor recebido
      return { result: adminData }
    } catch (e) {
      // notificamos o erro
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // registro
      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>

Essa página é muito semelhante à página [authentification]. As explicações são análogas tanto para sua execução pelo servidor [nuxt] quanto para sua execução pelo cliente [nuxt]. Observe, no entanto, que a linha 7 não exibe “sucesso” ou “falha”, como anteriormente, mas o valor dos dados recebidos do servidor de cálculo de impostos (linha 52):

Image

O resultado acima é obtido tanto pelo servidor quanto pelo cliente [nuxt]. Para provocar um erro, acesse a página [get-admindata], por meio do servidor ou do cliente [nuxt], sem estar autenticado:

Image

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) {
    // log
    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()
      // registro
      console.log('[fin-session asyncData response=]', response)
      // houve algum erro?
      if (response.état !== 400) {
        // o erro está em response.réponse
        throw new Error(response.réponse)
      }
      // o servidor enviou um novo cookie de sessão PHP
      // ele é recuperado tanto pelo servidor quanto 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 esse 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 encaminhe ao navegador
      const phpSessionCookie = dao.getPhpSessionCookie()
      // registramos no store que a sessão jSON foi iniciada e armazenamos o cookie de sessão PHP
      context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
      // o armazenamento é salvo 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) {
      // registro
      console.log('[fin-session asyncData error=]', e)
      // notificamos o erro
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // log
      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]')
    // somente 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 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 para o gerenciamento desse cookie diferem dependendo se é o servidor ou o cliente [nuxt] que executa esse código.

Comecemos pelo servidor [nuxt]:

  • linha 37: é a camada [dao] do servidor [nuxt] que é instanciada. Recorde-se o código de 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 precisa 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 o cookie anterior, caso a operação de encerramento da sessão tenha falhado;
  • linha 54: o cookie de sessão PHP é colocado no store e, em seguida, salvo 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 ele não executará a função [asyncData];
  • por fim, depois que o servidor e o cliente [nuxt] concluírem seu trabalho, sabemos que o cookie PHP, necessário para as trocas com o servidor de cálculo do imposto, está na sessão [nuxt];

O fato de o cookie PHP estar na sessão [nuxt] é suficiente para o servidor, pois é de lá que sua camada [dao] irá obtê-lo. No plugin [server/plgDao], que inicializa a camada [dao] do servidor, foi escrito:


/* 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)
  // registro
  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 história é outra. Na verdade, não é ele quem envia o cookie, mas sim o navegador que o executa. No entanto, esse 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]:

Image

O servidor de cálculo do imposto receberá do navegador um cookie de sessão PHP obsoleto e responderá que nenhuma sessão jSON está associada a esse cookie. Precisamos encontrar uma maneira de enviar ao navegador o novo cookie de sessão PHP.

Para isso, podemos utilizar um middleware de roteamento:

Image

O script [client/routing] é o middleware de roteamento declarado no arquivo [nuxt.config]:


// roteador
  router: {
    // raiz dos URL do aplicativo
    base: '/nuxt-12/',
    // middleware de roteamento
    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 esse 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 o cliente é roteado por meio de uma função importada na linha 4;

O script [middleware/client/routing] é o seguinte:


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

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

Voltemos à situação logo após a execução da página [fin-session] pelo servidor [nuxt]:

Image

Se clicarmos em um dos links do menu [3], o cliente [nuxt] assumirá o controle. 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 ele não estiver vazio, é transmitido ao navegador (linha 16). A partir desse momento, o navegador do cliente [nuxt] possui o cookie de sessão correto PHP;

O script [client/routing] é executado a cada mudança de página do cliente [nuxt]. O código do script é válido independentemente da página de destino: na maioria das vezes, ele simplesmente fornece ao navegador um cookie de sessão PHP que ele já possui, exceto em dois casos:

  • logo 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 [fin-session], conforme acabado de explicar;

Agora, vamos analisar o caso em que a página [fin-session] é executada apenas pelo cliente [nuxt], porque clicou-se em seu 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 de impostos
      const dao = context.app.$dao()
      const response = await dao.finSession()
      // registro
      console.log('[fin-session asyncData response=]', response)
      // houve algum erro?
      if (response.état !== 400) {
        // o erro está em response.réponse
        throw new Error(response.réponse)
      }
      // o servidor enviou um novo cookie de sessão PHP
      // ele é recuperado tanto pelo servidor quanto 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 esse 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 encaminhe ao navegador
      const phpSessionCookie = dao.getPhpSessionCookie()
      // registramos no store que a sessão jSON foi iniciada e armazenamos o cookie de sessão PHP
      context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
      // o armazenamento é salvo 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) {
      // registro
      console.log('[fin-session asyncData error=]', e)
      // notificamos o erro
      return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
    } finally {
      // registro
      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] é armazenado, colocado no store (linha 20) e, em seguida, salvo 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, é preciso ter o cuidado de, antes da execução, excluir o cookie de sessão [nuxt] e o cookie PHP do navegador que está executando o cliente [nuxt], a fim de partir de uma situação limpa. Abaixo, um exemplo com o navegador Chrome:

Image

15.20. Conclusion

Este exemplo foi particularmente complexo. Ele reuniu conhecimentos adquiridos nos exemplos anteriores: persistência do store em uma sessão [nuxt], plug-ins de injeção de funções, middleware de roteamento e gerenciamento de erros em operações assíncronas. A complexidade foi aumentada pelo fato de querermos que o usuário pudesse tanto usar os links do menu de navegação quanto digitar URL manualmente, sem que isso causasse falhas na aplicação. Para isso, fomos obrigados a analisar como cada página se comportava, dependendo se era executada pelo cliente ou pelo servidor [nuxt].

Essa uniformidade de comportamento entre o cliente e o 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 passa a operar no modo [SPA];

No entanto, mesmo nesse caso, é preciso verificar o resultado da execução de todas as páginas pelo servidor [nuxt], pois é isso que os mecanismos de busca obterão ao solicitá-las.