Skip to content

12. Funções HTTP do JavaScript

Image

12.1. Escolher uma biblioteca HTTP

Escolhemos duas bibliotecas aqui:

O ECMAScript 6 possui nativamente uma função HTTP chamada [fetch] que não está implementada no [node.js] (em setembro de 2019). Existe uma biblioteca chamada [node-fetch] que permite utilizar a função [fetch] no Node. Esta biblioteca utiliza determinadas APIs específicas do [node.js]. O código [node-fetch] pode, portanto, não ser 100% portátil para um ambiente que não seja [node], como um navegador;

Existe também uma biblioteca chamada [axios] dedicada a pedidos HTTP que é compatível tanto com [node.js] como com navegadores. Esta é a biblioteca que acabaremos por utilizar.

Apresentaremos o mesmo script escrito utilizando ambas as bibliotecas para demonstrar que a abordagem de codificação é semelhante.

12.2. Configurar um ambiente de desenvolvimento

12.2.1. Instalação do servidor de cálculo de impostos

Por fim, iremos escrever uma aplicação web com a seguinte arquitetura:

Image

JS: JavaScript

O código JavaScript é do lado do cliente:

  • de um serviço que fornece páginas ou fragmentos estáticos;
  • um serviço JSON;

O código JavaScript é, portanto, um cliente JSON e, como tal, pode ser organizado em camadas [UI, lógica de negócio, DAO] (UI: Interface do Utilizador), tal como os nossos clientes JSON escritos em PHP.

O servidor será o servidor de cálculo de impostos para o qual já escrevemos 13 versões. Vamos escrever uma 14.ª. Começamos, portanto, por duplicar, no NetBeans, a pasta da versão 13 para a pasta da versão 14:

Image

  • em [6], modificamos o ficheiro [config.json] da versão 14 da seguinte forma:

{
    "databaseFilename": "Config/database.json",
    "rootDirectory": "C:/myprograms/laragon-lite/www/php7/scripts-web/impots/version-14",
    "relativeDependencies": [
 
        "/Entities/BaseEntity.php",
        "/Entities/Simulation.php",
        ...
    "vues": {
        "vue-authentification.php": [700, 221, 400],
        "vue-calcul-impot.php": [200, 300, 341, 350, 800],
        "vue-liste-simulations.php": [500, 600]
    },
    "vue-erreurs": "vue-erreurs.php"
}
  • Na linha 3, alteramos o diretório raiz da aplicação;

Para aceder a este servidor, deve iniciar os serviços [Laragon].

Depois de fazer isto, podemos testar esta nova versão do servidor — que, neste momento, é idêntica à versão 13 — utilizando o [Postman] (ver artigo em link). Podemos utilizar o conjunto de pedidos usado para testar a versão 12 do servidor de cálculo de impostos:

Image

  • em [1-4], utilize a solicitação [init-session-700] para inicializar uma sessão JSON;
  • em [4-5], substitua [version-12] por [version-14] para testar a versão 14 do projeto;
  • após a execução, devemos receber a resposta JSON [6] do servidor;

A versão 14 do servidor está agora operacional. Teremos de a modificar ligeiramente. Vamos rever a API deste servidor:

Ação
Função
Contexto de execução
init-session
Utilizado para definir o tipo (json, xml, html) das respostas pretendidas
Pedido GET main.php?action=init-session&type=x
pode ser enviada a qualquer momento
authenticate-user
Autoriza ou recusa o login de um utilizador
Pedido POST main.php?action=authenticate-user
A solicitação deve conter dois parâmetros postados [user, password]
Só pode ser emitida se o tipo de sessão (json, xml, html) for conhecido
calculate-tax
Realiza uma simulação de cálculo de impostos
Pedido POST para main.php?action=calculate-tax
A solicitação deve conter três parâmetros enviados [casado, filhos, salário]
Só pode ser emitida se o tipo de sessão (json, xml, html) for conhecido e o utilizador estiver autenticado
list-simulations
Pedido para visualizar a lista de simulações realizadas desde o início da sessão
Pedido GET main.php?action=list-simulations
A solicitação não aceita nenhum outro parâmetro
Só pode ser emitida se o tipo de sessão (json, xml, html) for conhecido e o utilizador estiver autenticado
delete-simulation
Elimina uma simulação da lista de simulações
Pedido GET main.php?action=list-simulations&number=x
A solicitação não aceita nenhum outro parâmetro
Só pode ser emitida se o tipo de sessão (json, xml, html) for conhecido e o utilizador estiver autenticado
end-session
Encerra a sessão de simulação.
Tecnicamente, a sessão web antiga é eliminada e é criada uma nova sessão
Só pode ser emitida se o tipo de sessão (json, xml, html) for conhecido e o utilizador estiver autenticado

12.2.2. Instalação das bibliotecas HTTP do cliente JavaScript

Inicialmente, trabalharemos com a seguinte arquitetura:

Image

  • Em [1], um script de consola [node.js] faz uma solicitação HTTP ao servidor JSON de cálculo de impostos;
  • Em [4], recebe essa resposta e apresenta-a na consola;

No Exemplo 1, utilizaremos as bibliotecas [node-fetch] e [axios] e, posteriormente, manteremos apenas a [axios] para os exemplos seguintes. Iremos agora instalar estas duas bibliotecas JavaScript a partir do terminal do [VSCode]:

Image

Iremos também utilizar a biblioteca [qs], que permite a codificação de uma string para URL. Recorde-se que esta codificação é utilizada para codificar os parâmetros de um pedido HTTP GET ou POST.

Image

12.3. script [fetch-01]

O script [fetch-01] utiliza a biblioteca [node-fetch] para inicializar uma sessão JSON com o servidor de cálculo de impostos. O seu código é o seguinte:


'use strict';
 
// imports
import fetch from 'node-fetch';
import qs from 'qs';
import { sprintf } from 'sprintf-js';
import moment from 'moment';
 
 
// URL base of tax calculation server
const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
// init session
async function initSession() {
  // query options HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    timeout: 2000
  };
  // execute query HTTP [get /main.php?action=init-session&type=json]
  let débutFetch;
  try {
    // asynchronous request - [fetch] makes a promise
    débutFetch = moment(Date.now());
    const response = await fetch(baseUrl + qs.stringify({
      action: 'init-session',
      type: 'json'
    }), options);
    // [response] is the entire HTTP response from the server (HTTP headers + response itself)
    // display this answer to see its structure
    console.log(sprintf("réponse fetch formatée en json,=%j, %s", response, heure(débutFetch)));
    console.log("réponse fetch en javascript=", response);
    // you can have HTTP headers
    console.log("entêtes de la réponse=", response.headers);
    // if application/json response, the server's json response is obtained with the asynchronous function [response.json()]
    // in which case the calling code obtains a [Promise] object
    // [await] allows you to obtain the server's [json] response rather than its promise
    const débutJson = moment(Date.now());
    const objet = await response.json();
    console.log(sprintf("réponse json=%j, type=%s, %s", objet, typeof (objet), heure(débutJson)));
    return objet;
    // if text / plain, the text response from the server is obtained with [response.text()]
    // in which case the calling code obtains a [Promise] object
    // [await] allows you to obtain the server's [text] response rather than its promise
    // const text = await response.text();
    // console.log("answer text=", text);
    // return text;
  } catch (error) {
    // we're here because the server has sent an error code [404 Not Found, ...] accompanied by an empty body - we display the error to see its structure
    // or because the [fetch] client has thrown an exception (network inaccessible, ...)
    // the error structure is displayed
    console.log(sprintf("error fetch en json=%j, %s", error, heure(débutFetch)));
    console.log("error fetch en javascript=", typeof (error), error);
    // launch the error message received
    throw error.message;
  }
}
 
// the main function executes the asynchronous function [initSession]
async function main() {
  try {
    console.log("requête HTTP vers le serveur en cours ---------------------------------------------");
    const response = await initSession();
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response, typeof (response))
  } catch (error) {
    console.log("erreur ---------------------------------------------");
    console.log("erreur=", error, typeof (error));
  }
}
 
// test
main();
 
// time and duration display utility
function heure(début) {
  // current time
  const now = moment(Date.now());
  // time formatting
  let result = "heure=" + now.format("HH:mm:ss:SSS");
  // is it necessary to calculate a duration?
  if (début) {
    const durée = now - début;
    const milliseconds = durée % 1000;
    const seconds = Math.floor(durée / 1000);
    // format time + duration
    result = result + sprintf(", durée= %s seconde(s) et %s millisecondes", seconds, milliseconds);
  }
  // result
  return result;
}

Comentários

  • As funções HTTP do JavaScript são funções assíncronas. Aqui, estamos a aplicar o que aprendemos na secção anterior (ver link);
  • linha 24: para aguardar que a resposta da função assíncrona [fetch] seja publicada no ciclo de eventos [node.js], usamos a palavra-chave [await]. Sabemos que esta instrução deve estar dentro de código precedido pela palavra-chave [async] (linha 13);
  • linhas 13–56: encapsulamos o código HTTP dentro da função assíncrona [initSession];
  • linhas 59–69: uma segunda função assíncrona [main] é utilizada para chamar a função assíncrona [initSession] de forma bloqueante (async/await);
  • linha 72: a função assíncrona [main] é chamada;
  • embora todo o código se assemelhe a código síncrono, estas são, de facto, funções assíncronas que são executadas, mas de forma bloqueante;
  • linha 19: para inicializar uma sessão JSON com o servidor de cálculo de impostos, deve enviar-lhe o pedido HTTP [get /main.php?action=init-session&type=json]. É isto que o código nas linhas 24–27 faz. A sintaxe para [fetch] é a seguinte: [fetch(URL, options)] com:
    • [URL]: a URL que está a ser consultada;
    • [options]: um objeto que define as opções da solicitação. É aqui que definimos os cabeçalhos HTTP que queremos enviar ao servidor de destino;
  • linhas 15–18: definimos as opções para a solicitação que queremos fazer:
    • [method]: queremos realizar um GET;
    • [timeout]: queremos que o cliente [fetch] espere no máximo 2 segundos pela resposta do servidor. Se este tempo limite for excedido, [fetch] lançará uma exceção;
  • linha 24: para obter a URL [/main.php?action=init-session&type=json], usamos a biblioteca [qs] para obter os parâmetros codificados na URL [action,type] da solicitação GET. A string resultante é [init-session&type=json], que poderíamos ter construído nós mesmos. Queríamos simplesmente demonstrar como obter uma string codificada na URL;
  • linha 24: a palavra-chave [await] indica que uma tarefa assíncrona está a ser iniciada aqui e que estamos à espera que ela publique a sua resposta no ciclo de eventos [node.js];
  • linha 24: em [response], obtemos um objeto complexo que descreve toda a resposta HTTP recebida (cabeçalhos e corpo);
  • linhas 30–31: Imprimimos o objeto [response] para ver a sua estrutura, primeiro como uma string e depois como um objeto JavaScript;
  • linha 33: exibimos os cabeçalhos HTTP enviados pelo servidor;
  • Linha 38: Sabemos que o servidor de cálculo de impostos enviará uma string JSON. Esta string está encapsulada no objeto [response]. Podemos recuperá-la utilizando o método [response.json()]. No entanto, este método é assíncrono. Por isso, escrevemos [await response.json()] para recuperar a string JSON que será publicada no ciclo de eventos [node.js]. Na verdade, não é a string JSON em si que obtemos, mas o objeto JavaScript por ela representado;
  • linha 39: exibição da string JSON recebida;
  • linha 40: devolve o objeto JavaScript recebido;
  • linha 47: capturamos quaisquer erros potenciais da instrução [fetch]. Esta instrução apenas lança uma exceção se a operação HTTP falhar e não for recebida qualquer resposta do servidor. Se for recebida uma resposta, mesmo com um código de estado HTTP diferente de [200 OK], [fetch] não lança uma exceção, e a resposta do servidor estará disponível na linha 38;
  • linhas 51–52: o objeto [error] recebido pela cláusula [catch] é exibido, primeiro como uma string JSON e depois como um objeto JavaScript;
  • linha 54: a mensagem de erro de [fetch] é armazenada em [error.message];
  • linhas 59–69: a função assíncrona [main] chama a função assíncrona [initSession] de forma bloqueante (await na linha 62);
  • linha 72: a função assíncrona [main] é iniciada e o código do script principal fica então concluído. O script completo terminará quando as tarefas assíncronas iniciadas tiverem publicado os seus resultados no ciclo de eventos;

Os resultados da execução são os seguintes:

Caso 1: o servidor Laragon não está em execução


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-01.js"
requête HTTP vers le serveur en cours ---------------------------------------------
error fetch en json={"message":"network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json","type":"request-timeout"}, heure=10:08:48:180, durée= 2 seconde(s) et 62 millisecondes
error fetch en javascript= object { FetchError: network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json
    at Timeout.<anonymous> (c:\Data\st-2019\dev\es6\javascript\node_modules\node-fetch\lib\index.js:1448:13)
    at ontimeout (timers.js:436:11)
    at tryOnTimeout (timers.js:300:5)
    at listOnTimeout (timers.js:263:5)
    at Timer.processTimers (timers.js:223:10)
  message:
   'network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
  type: 'request-timeout' }
erreur ---------------------------------------------
erreur= network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json string
 
[Done] exited with code=0 in 2.804 seconds

Comentários

  • linha 3: o pedido HTTP falha após 2 segundos e 62 milissegundos devido ao tempo limite de 2 segundos imposto ao pedido HTTP;
  • linhas 4–9: o objeto JavaScript [error] interceptado pela cláusula [catch(error)]. Este objeto tem duas propriedades:
    • [FetchError]: linha 4;
    • [message]: linhas 10–12;
  • linha 14: a mensagem de erro recebida pela função assíncrona [main];

Caso 2: O servidor Laragon está em execução


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-01.js"
requête HTTP vers le serveur en cours ---------------------------------------------
réponse fetch formatée en json,={"size":0,"timeout":2000}, heure=10:13:50:814, durée= 0 seconde(s) et 375 millisecondes
réponse fetch en javascript= Response {
  size: 0,
  timeout: 2000,
  [Symbol(Body internals)]:
   { body:
      PassThrough {
        _readableState: [ReadableState],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: true,
        _transformState: [Object] },
     disturbed: false,
     error: null },
  [Symbol(Response internals)]:
   { url:
      'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
     status: 200,
     statusText: 'OK',
     headers: Headers { [Symbol(map)][Object] },
     counter: 0 } }
entêtes de la réponse= Headers {
  [Symbol(map)]:
   [Object: null prototype] {
     date: [ 'Sat, 14 Sep 2019 08:13:50 GMT' ],
     server: [ 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11' ],
     'x-powered-by': [ 'PHP/7.2.11' ],
     'cache-control': [ 'max-age=0, private, must-revalidate, no-cache, private' ],
     'set-cookie': [ 'PHPSESSID=99q2iinusmhl55fa600aie2mmu; path=/' ],
     'content-length': [ '86' ],
     connection: [ 'close' ],
     'content-type': [ 'application/json' ] } }
réponse json={"action":"init-session","état":700,"réponse":"session démarrée avec type [json]"}, type=object, heure=10:13:50:825, durée= 0 seconde(s) et 1 millisecondes
succès ---------------------------------------------
réponse= { action: 'init-session',
  'état': 700,
  'réponse': 'session démarrée avec type [json]' } object
 
[Done] exited with code=0 in 1.022 seconds

Comentários

  • linha 3: [fetch] recebe a resposta do servidor após 375 ms;
  • linhas 4–39: a estrutura do objeto JavaScript [response] que encapsula a resposta do servidor. Entre as suas propriedades, algumas podem ser do nosso interesse:
    • [status] (linha 25): código de estado HTTP da resposta do servidor;
    • [statusText] (linha 26): texto associado a este código;
    • [headers] (linha 27): os cabeçalhos HTTP da resposta do servidor;
    • [body] (linha 8): representa o documento enviado pelo servidor. A instrução [fetch] fornece métodos para trabalhar com ele;
  • linhas 29–39: os cabeçalhos HTTP da resposta do servidor;
  • linha 40: a função assíncrona [response.json()] devolveu a sua resposta após 1 milésimo de segundo;
  • linhas 42–44: o objeto JavaScript recebido pela função assíncrona [main];

Caso 3: O servidor Laragon está em execução, mas é-lhe enviado um comando incorreto:

Image

  • acima, na linha 26, é passado um tipo de sessão incorreto para o servidor;

Os resultados da execução são os seguintes:


requête HTTP vers le serveur en cours ---------------------------------------------
réponse fetch formatée en json,={"size":0,"timeout":2000}, heure=10:27:54:114, durée= 0 seconde(s) et 136 millisecondes
réponse fetch en javascript= Response {
  size: 0,
  timeout: 2000,
  [Symbol(Body internals)]:
   { body:
      PassThrough {
        _readableState: [ReadableState],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: true,
        _transformState: [Object] },
     disturbed: false,
     error: null },
  [Symbol(Response internals)]:
   { url:
      'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=x',
     status: 400,
     statusText: 'Bad Request',
     headers: Headers { [Symbol(map)][Object] },
     counter: 0 } }
entêtes de la réponse= Headers {
  [Symbol(map)]:
   [Object: null prototype] {
     date: [ 'Sat, 14 Sep 2019 08:27:54 GMT' ],
     server: [ 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11' ],
     'x-powered-by': [ 'PHP/7.2.11' ],
     'cache-control': [ 'max-age=0, private, must-revalidate, no-cache, private' ],
     'set-cookie': [ 'PHPSESSID=5ku9gfok81ikj98hia0meeum57; path=/' ],
     'content-length': [ '79' ],
     connection: [ 'close' ],
     'content-type': [ 'application/json' ] } }
réponse json={"action":"init-session","état":703,"réponse":"paramètre type=[x] invalide"}, type=object, heure=10:27:54:127, durée= 0 seconde(s) et 2 millisecondes
succès ---------------------------------------------
réponse= { action: 'init-session',
  'état': 703,
  'réponse': 'paramètre type=[x] invalide' } object
 
[Done] exited with code=0 in 0.712 seconds
  • A resposta do servidor é recebida na linha 2;
  • linha 24: podemos ver que o código de estado HTTP da resposta do servidor é 400, um código de erro. No entanto, [fetch] não lançou uma exceção. Desde que [fetch] receba uma resposta do servidor, processa-a e não lança uma exceção;
  • linhas 41–43: a resposta obtida pela função assíncrona [main];

12.4. script [fetch-02]

O script a seguir reutiliza o script [fetch-01], eliminando todos os detalhes desnecessários:


'use strict';
 
// imports
import fetch from 'node-fetch';
import qs from 'qs';
 
// URL base of tax calculation server
const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
// init session
async function initSession() {
  // query options HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    timeout: 2000
  };
  // execute query HTTP [get /main.php?action=init-session&type=json]
  const response = await fetch(baseUrl + qs.stringify({
    action: 'init-session',
    type: 'json'
  }), options);
  // result received in jSON
  return await response.json();
}
 
// the main function executes the asynchronous function [initSession]
async function main() {
  try {
    console.log("requête HTTP vers le serveur en cours ---------------------------------------------");
    const response = await initSession();
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response)
  } catch (error) {
    console.log("erreur ---------------------------------------------");
    console.log("erreur=", error.message);
  }
}
 
// test
main();

Resultados de uma execução normal:


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-02.js"
requête HTTP vers le serveur en cours ---------------------------------------------
succès ---------------------------------------------
réponse= { action: 'init-session',
  'état': 700,
  'réponse': 'session démarrée avec type [json]' }
 
[Done] exited with code=0 in 0.56 seconds

Resultados de uma execução com uma exceção (parando o servidor Laragon):


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-02.js"
requête HTTP vers le serveur en cours ---------------------------------------------
erreur ---------------------------------------------
erreur= network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json
 
[Done] exited with code=0 in 2.701 seconds

12.5. script [axios-01]

Aqui, retomamos o script [fetch-01], que reescrevemos utilizando a biblioteca [axios]. Recorde-se que o nosso interesse nesta biblioteca reside na sua portabilidade entre o ambiente [node.js] e os navegadores comuns. Isto permite:

  • Na Fase 1, testar os nossos scripts num ambiente [Node.js];
  • na fase 2, portá-los para um navegador;

O script [axios-01] segue a estrutura do script [fetch-01]:


'use strict';
import axios from 'axios';
 
// axios default configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';

// init session
async function initSession(axios) {
  // 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 [get /main.php?action=init-session&type=json]
  try {
    // asynchronous request
    const response = await axios.request('main.php', options);
    // response is the entire HTTP response from the server (HTTP headers + response itself)
    // display this answer to see its structure
    console.log("réponse axios=", response);
    // the server response is in [response.data]
    return response.data;
  } catch (error) {
    // we're here because the server has sent an error code [404 Not Found, 500 Internal Server Error, ...]
    // the [error] parameter is an exception instance - it can take various forms
    // display it to see its structure
    console.log("axios error=", typeof (error), error);
    if (error.response) {
      // the server reported an error in status HTTP but also sent a response
      // then it is found in [error.response.data]
      // we know that the server sends jSON responses with structure {action, status, response}
      // and that in the event of an error, the error msg is in [reply]
      return error.response.data;
    } else {
      // we launch the error
      throw error;
    }
  }
}
 
// the main function executes the asynchronous function [initSession]
async function main() {
  try {
    console.log("requête HTTP vers le serveur en cours ---------------------------------------------");
    const response = await initSession(axios);
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response, typeof (response))
  } catch (error) {
    console.log("erreur ---------------------------------------------");
    console.log("erreur=", error.message);
  }
}
 
// test
main();

Comentários

  • linha 2: importamos a biblioteca [axios];
  • linhas 5-6: configuração padrão para pedidos HTTP. As opções [axios.defaults] aplicam-se a todos os pedidos HTTP enviados pelo objeto [axios] sem necessidade de serem especificadas para cada novo pedido;
  • linha 5: tempo limite de 2 segundos para todas as solicitações;
  • linha 6: todas as URLs serão relativas à URL base;
  • linha 9: a função assíncrona [initSession];
  • linhas 11–18: as opções para a solicitação HTTP a ser enviada (além das opções padrão já definidas nas linhas 5–6);
  • linhas 14–17: os parâmetros de URL [action=init-session&type=json]. O objeto [params] será automaticamente convertido numa string codificada para URL;
  • linha 22: chamada de bloqueio à função assíncrona [axios.request]. O primeiro parâmetro é a URL de destino construída como [main.php] anexada à URL base definida na linha 6. O segundo parâmetro é o objeto [options] das linhas 11–18;
  • linha 25: [response] é um objeto JavaScript que encapsula toda a resposta HTTP do servidor (cabeçalhos HTTP + corpo da resposta). Exibimo-lo para ver a sua estrutura JavaScript;
  • linha 27: se o servidor enviou um documento, este encontra-se em [response.data]. Aqui sabemos que o servidor envia uma resposta JSON acompanhada pelo cabeçalho HTTP [Content-type: application/json]. A presença deste cabeçalho faz com que [axios] deserialize automaticamente [response.data] num objeto JavaScript;
  • linha 28: a função [axios] pode lançar uma exceção. É aqui que o [axios] difere do [fetch]. É lançada uma exceção nos seguintes casos:
    • a solicitação HTTP não pôde ser enviada (erro do lado do cliente);
    • o servidor enviou um código de erro HTTP (400, 404, 500, …) (este comportamento é, na verdade, configurável). Note que, se este código de estado HTTP for acompanhado por uma resposta, o [fetch] não lançou uma exceção, ao passo que o [axios] o faz. No entanto, se o código de erro HTTP for acompanhado por um documento, este é colocado em [error.response];
  • linha 32: exibimos a estrutura JavaScript do objeto [error];
  • linhas 33–38: se o objeto [error] contiver um objeto [response], essa resposta é devolvida ao código de chamada;
  • linhas 39–42: em todos os outros casos, o objeto [error] é passado de volta para o código de chamada;
  • linhas 47–57: a função assíncrona [main];
  • linha 50: chamada bloqueante à função assíncrona [initSession]. A resposta JSON do servidor é recuperada como um objeto JavaScript;
  • linhas 53–56: tratamento de eventuais erros. A mensagem de erro encontra-se em [error.message];

Os resultados da execução são os seguintes:

Caso 1: O servidor Laragon não está a funcionar


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
requête HTTP vers le serveur en cours ---------------------------------------------
axios error= object { Error: timeout of 2000ms exceeded
    at createError (c:\Data\st-2019\dev\es6\javascript\node_modules\axios\lib\core\createError.js:16:15)
    at Timeout.handleRequestTimeout (c:\Data\st-2019\dev\es6\javascript\node_modules\axios\lib\adapters\http.js:252:16)
    at ontimeout (timers.js:436:11)
    at tryOnTimeout (timers.js:300:5)
    at listOnTimeout (timers.js:263:5)
    at Timer.processTimers (timers.js:223:10)
  config:
   { url:
      'http://localhost/php7/scripts-web/impots/version-14/main.php',
     method: 'get',
     params: { action: 'init-session', type: 'json' },
     headers:
      { Accept: 'application/json, text/plain, */*',
        'User-Agent': 'axios/0.19.0' },
     baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
     transformRequest: [ [Function: transformRequest] ],
     transformResponse: [ [Function: transformResponse] ],
     timeout: 2000,
     adapter: [Function: httpAdapter],
     xsrfCookieName: 'XSRF-TOKEN',
     xsrfHeaderName: 'X-XSRF-TOKEN',
     maxContentLength: -1,
     validateStatus: [Function: validateStatus],
     data: undefined },
  code: 'ECONNABORTED',
  request:
   Writable {
     _writableState:
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        finalCalled: false,
        needDrain: false,
        ending: false,
        ended: false,
        finished: false,
        destroyed: false,
        decodeStrings: true,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: true,
        bufferProcessing: false,
        onwrite: [Function: bound onwrite],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 0,
        prefinished: false,
        errorEmitted: false,
        emitClose: true,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: true,
     domain: null,
     _events:
      [Object: null prototype] {
        response: [Function: handleResponse],
        error: [Function: handleRequestError] },
     _eventsCount: 2,
     _maxListeners: undefined,
     _options:
      { protocol: 'http:',
        maxRedirects: 21,
        maxBodyLength: 10485760,
        path:
         '/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
        method: 'GET',
        headers: [Object],
        agent: undefined,
        auth: undefined,
        hostname: 'localhost',
        port: null,
        nativeProtocols: [Object],
        pathname: '/php7/scripts-web/impots/version-14/main.php',
        search: '?action=init-session&type=json' },
     _redirectCount: 0,
     _redirects: [],
     _requestBodyLength: 0,
     _requestBodyBuffers: [],
     _onNativeResponse: [Function],
     _currentRequest:
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 6,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: true,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: false,
        sendDate: false,
        _removedConnection: false,
        _removedContLen: false,
        _removedTE: false,
        _contentLength: 0,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Socket],
        connection: [Socket],
        _header:
         'GET /php7/scripts-web/impots/version-14/main.php?action=init-session&type=json HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.19.0\r\nHost: localhost\r\nConnection: close\r\n\r\n',
        _onPendingData: [Function: noopPendingOutput],
        agent: [Agent],
        socketPath: undefined,
        timeout: undefined,
        method: 'GET',
        path:
         '/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
        _ended: false,
        res: null,
        aborted: 1568528450762,
        timeoutCb: null,
        upgradeOrConnect: false,
        parser: [HTTPParser],
        maxHeadersCount: null,
        _redirectable: [Circular],
        [Symbol(isCorked)]: false,
        [Symbol(outHeadersKey)][Object] },
     _currentUrl:
      'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json' },
  response: undefined,
  isAxiosError: true,
  toJSON: [Function] }
erreur ---------------------------------------------
erreur= timeout of 2000ms exceeded
 
[Done] exited with code=0 in 2.784 seconds

Comentários

  • linhas 33-136: o objeto [error] contém muita informação;
    • [Error], linhas 3-9: uma descrição do erro que ocorreu;
    • [config], linhas 10–27: a configuração do pedido HTTP que levou a este erro;
    • [config.url], linhas 11–12: o URL de destino;
    • [config.method], linha 13: método da solicitação;
    • [config.params], linha 14: os parâmetros da URL;
    • [config.headers], linhas 16–17: os cabeçalhos HTTP da solicitação;
    • [config.baseURL], linha 18: o URL base do URL de destino;
    • [config.timeout], linha 21: o tempo limite da solicitação;
    • [code], linha 28: um código de erro;
    • [request], linhas 29–133: uma descrição detalhada da solicitação HTTP. Note que a maioria das propriedades é precedida por um sublinhado (_), indicando que são propriedades internas do objeto [request] não destinadas a serem usadas diretamente pelo programador;
    • [response], linha 134: a resposta do servidor, que aqui está vazia;
  • linha 138: a mensagem de erro exibida pela função [main];

Caso 2: O servidor Laragon está em execução


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
requête HTTP vers le serveur en cours ---------------------------------------------
réponse axios= { status: 200,
  statusText: 'OK',
  headers:
   { date: 'Sun, 15 Sep 2019 07:09:26 GMT',
     server: 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11',
     'x-powered-by': 'PHP/7.2.11',
     'cache-control': 'max-age=0, private, must-revalidate, no-cache, private',
     'set-cookie': [ 'PHPSESSID=uas6lugtblstktcifpd8e5irm6; path=/' ],
     'content-length': '86',
     connection: 'close',
     'content-type': 'application/json' },
  config:
   { url:
      'http://localhost/php7/scripts-web/impots/version-14/main.php',
     method: 'get',
     params: { action: 'init-session', type: 'json' },
     headers:
      { Accept: 'application/json, text/plain, */*',
        'User-Agent': 'axios/0.19.0' },
     baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
     transformRequest: [ [Function: transformRequest] ],
     transformResponse: [ [Function: transformResponse] ],
     timeout: 2000,
     adapter: [Function: httpAdapter],
     xsrfCookieName: 'XSRF-TOKEN',
     xsrfHeaderName: 'X-XSRF-TOKEN',
     maxContentLength: -1,
     validateStatus: [Function: validateStatus],
     data: undefined },
  request:
   ClientRequest {
     domain: null,
     _events:
      [Object: null prototype] {
        socket: [Function],
        abort: [Function],
        aborted: [Function],
        error: [Function],
        timeout: [Function],
        prefinish: [Function: requestOnPrefinish] },
     _eventsCount: 6,
     _maxListeners: undefined,
     output: [],
     outputEncodings: [],
     outputCallbacks: [],
     outputSize: 0,
     writable: true,
     _last: true,
     chunkedEncoding: false,
     shouldKeepAlive: false,
     useChunkedEncodingByDefault: false,
     sendDate: false,
     _removedConnection: false,
     _removedContLen: false,
     _removedTE: false,
     _contentLength: 0,
     _hasBody: true,
     _trailer: '',
     finished: true,
     _headerSent: true,
     socket:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [TCP],
        _parent: null,
        _host: 'localhost',
        _readableState: [ReadableState],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 7,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: false,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: null,
        _server: null,
        parser: null,
        _httpMessage: [Circular],
        [Symbol(asyncId)]: 6,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]: null,
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0 },
     connection:
      Socket {
        connecting: false,
        _hadError: false,
        _handle: [TCP],
        _parent: null,
        _host: 'localhost',
        _readableState: [ReadableState],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 7,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: false,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: null,
        _server: null,
        parser: null,
        _httpMessage: [Circular],
        [Symbol(asyncId)]: 6,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]: null,
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0 },
     _header:
      'GET /php7/scripts-web/impots/version-14/main.php?action=init-session&type=json HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.19.0\r\nHost: localhost\r\nConnection: close\r\n\r\n',
     _onPendingData: [Function: noopPendingOutput],
     agent:
      Agent {
        domain: null,
        _events: [Object],
        _eventsCount: 1,
        _maxListeners: undefined,
        defaultPort: 80,
        protocol: 'http:',
        options: [Object],
        requests: {},
        sockets: [Object],
        freeSockets: {},
        keepAliveMsecs: 1000,
        keepAlive: false,
        maxSockets: Infinity,
        maxFreeSockets: 256 },
     socketPath: undefined,
     timeout: undefined,
     method: 'GET',
     path:
      '/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
     _ended: true,
     res:
      IncomingMessage {
        _readableState: [ReadableState],
        readable: false,
        domain: null,
        _events: [Object],
        _eventsCount: 3,
        _maxListeners: undefined,
        socket: [Socket],
        connection: [Socket],
        httpVersionMajor: 1,
        httpVersionMinor: 0,
        httpVersion: '1.0',
        complete: true,
        headers: [Object],
        rawHeaders: [Array],
        trailers: {},
        rawTrailers: [],
        aborted: false,
        upgrade: false,
        url: '',
        method: null,
        statusCode: 200,
        statusMessage: 'OK',
        client: [Socket],
        _consuming: false,
        _dumped: false,
        req: [Circular],
        responseUrl:
         'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
        redirects: [] },
     aborted: undefined,
     timeoutCb: null,
     upgradeOrConnect: false,
     parser: null,
     maxHeadersCount: null,
     _redirectable:
      Writable {
        _writableState: [WritableState],
        writable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _options: [Object],
        _redirectCount: 0,
        _redirects: [],
        _requestBodyLength: 0,
        _requestBodyBuffers: [],
        _onNativeResponse: [Function],
        _currentRequest: [Circular],
        _currentUrl:
         'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json' },
     [Symbol(isCorked)]: false,
     [Symbol(outHeadersKey)]:
      [Object: null prototype] { accept: [Array], 'user-agent': [Array], host: [Array] } },
  data:
   { action: 'init-session',
     'état': 700,
     'réponse': 'session démarrée avec type [json]' } }
succès ---------------------------------------------
réponse= { action: 'init-session',
  'état': 700,
  'réponse': 'session démarrée avec type [json]' } object
 
[Done] exited with code=0 in 1.115 seconds

Comentários

  • linhas 3–203: o objeto JavaScript [response] que encapsula a resposta HTTP do servidor;
  • linha 3, [status]: o código de estado HTTP da resposta;
  • linha 4, [statusText]: o texto associado ao código de estado HTTP anterior;
  • linhas 5-13, [headers]: os cabeçalhos HTTP da resposta:
    • linha 10, [Set-Cookie]: o cookie de sessão;
    • linha 13, [Content-Type]: o tipo de documento enviado pelo servidor;
  • linhas 14–31, [config]: a configuração do pedido HTTP enviado;
  • linhas 32–199, [request]: o objeto JavaScript que detalha a solicitação HTTP enviada;
  • linhas 200–203, [request.data]: o objeto JavaScript que encapsula a resposta JSON do servidor;
  • linhas 205–207: a resposta recuperada pela função assíncrona [main];

Caso 3: É enviada uma solicitação [init-session] incorreta para o servidor Laragon;

Image

Os resultados da execução são os seguintes:


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
requête HTTP vers le serveur en cours ---------------------------------------------
axios error= object { Error: Request failed with status code 400
   ...
  config:
   { url:
      'http://localhost/php7/scripts-web/impots/version-14/main.php',
     ...
     data: undefined },
  request:
   ...
     [Symbol(outHeadersKey)]:
      [Object: null prototype] { accept: [Array], 'user-agent': [Array], host: [Array] } },
  response:
   { status: 400,
     statusText: 'Bad Request',
     headers:
      { date: 'Sun, 15 Sep 2019 07:25:58 GMT',
        server: 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11',
        'x-powered-by': 'PHP/7.2.11',
        'cache-control': 'max-age=0, private, must-revalidate, no-cache, private',
        'set-cookie': [Array],
        'content-length': '79',
        connection: 'close',
        'content-type': 'application/json' },
     config:
      { url:
         'http://localhost/php7/scripts-web/impots/version-14/main.php',
        ...
        data: undefined },
     request:
      ...
        [Symbol(outHeadersKey)][Object] },
     data:
      { action: 'init-session',
        'état': 703,
        'réponse': 'paramètre type=[x] invalide' } },
  isAxiosError: true,
  toJSON: [Function] }
succès ---------------------------------------------
réponse= { action: 'init-session',
  'état': 703,
  'réponse': 'paramètre type=[x] invalide' } object
 
[Done] exited with code=0 in 0.69 seconds

Como o servidor respondeu com um código HTTP 400 (linha 15), o [axios] lançou uma exceção (mais uma vez, este comportamento é configurável). Embora o [axios] tenha lançado uma exceção, ainda obtemos a resposta HTTP do servidor em [error.response] (linha 14) e o documento JSON enviado em [error.response.data] (linha 34). Linhas 41–43: a função [main] recupera corretamente a resposta JSON do servidor.

12.6. script [axios-02]

Agora que detalhámos os objetos tratados pela biblioteca [axios] durante um pedido HTTP, podemos reescrever o script [axios-01] da seguinte forma:


'use strict';
import axios from 'axios';
 
// axios default configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
 
// init session
async function initSession(axios) {
  // query options HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    // URL parameters
    params: {
      action: 'init-session',
      type: 'json'
    }
  };
  try {
    // execute query HTTP [get /main.php?action=init-session&type=json]
    const response = await axios.request('main.php', options);
    // the server response is in [response.data]
    return response.data;
  } catch (error) {
    // server response
    if (error.response) {
      // the answer jSON is in [error.response.data]
      return error.response.data;
    } else {
      // error restart
      throw error;
    }
  }
}
 
// the main function executes the asynchronous function [initSession]
async function main() {
  try {
    console.log("requête HTTP vers le serveur en cours ---------------------------------------------");
    const response = await initSession(axios);
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response, typeof (response))
  } catch (error) {
    console.log("erreur ---------------------------------------------");
    console.log("erreur=", error.message);
  }
}
 
// test
main();

12.7. script [axios-03]

O script [axios-03] segue a mesma metodologia do script [axios-02]. Desta vez, adicionamos a solicitação HTTP [authenticate-user] ao servidor, que é feita utilizando uma solicitação POST:


'use strict';
import axios from 'axios';
import qs from 'qs'
 
// axios configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
 
 
// init session
async function initSession(axios) {
  // query options HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    // URL parameters
    params: {
      action: 'init-session',
      type: 'json'
    }
  };
  try {
    // execute query HTTP [get /main.php?action=init-session&type=json]
    const response = await axios.request('main.php', options);
    // the server response is in [response.data]
    return response.data;
  } catch (error) {
    // server response
    if (error.response) {
      // the answer jSON is in [error.response.data]
      return error.response.data;
    } else {
      // error restart
      throw error;
    }
  }
}
 
async function authentifierUtilisateur(axios, 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'
    }
  };
  try {
    // execute query HTTP [post /main.php?action=authenticate-user]
    const response = await axios.request('main.php', options);
    // the server response is in [response.data]
    return response.data;
  } catch (error) {
    // server response
    if (error.response) {
      // the answer jSON is in [error.response.data]
      return error.response.data;
    } else {
      // error restart
      throw error;
    }
  }
}
 
// the main function executes asynchronous functions one by one
async function main() {
  try {
    // init-session
    console.log("action init-session en cours ---------------------------------------------");
    const response1 = await initSession(axios);
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response1);
    // authenticate-user
    console.log("action authentifier-utilisateur en cours ---------------------------------------------");
    const response2 = await authentifierUtilisateur(axios, 'admin', 'admin');
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response2)
  } catch (error) {
    console.log("erreur ---------------------------------------------");
    console.log("erreur=", error);
  }
}
 
// test
main();

Comentários

  • linhas 38-70: a função assíncrona [authenticateUser];
  • linha 39: o pedido deve ser feito [POST /main.php?action=authenticate-user];
  • linhas 40–54: as opções da solicitação HTTP;
  • linha 41: esta é uma solicitação POST;
  • linhas 42–44: os parâmetros POST serão codificados por URL num documento que o cliente envia com a sua solicitação;
  • linhas 46–49: a propriedade [data] deve conter a string POST codificada por URL. Para isso, usamos a biblioteca [qs] importada na linha 3;
  • linhas 55–69: para executar a solicitação, usamos o mesmo código do método [initSession];
  • linhas 73–89: o método [asynchrone] chama sucessivamente os métodos [initSession] e [authentifierUtilisateur] de forma bloqueante, linhas 77 e 82;
  • linha 82: o par (admin, admin) é utilizado como credenciais de login. Sabemos que são reconhecidas pelo servidor;

Os resultados da execução são os seguintes:


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-03.js"
action init-session en cours ---------------------------------------------
succès ---------------------------------------------
réponse= { action: 'init-session',
  'état': 700,
  'réponse': 'session démarrée avec type [json]' }
action authentifier-utilisateur en cours ---------------------------------------------
succès ---------------------------------------------
réponse= { action: 'authentifier-utilisateur',
  'état': 103,
  'réponse':
   [ 'pas de session en cours. Commencer par action [init-session]' ] }
 
[Done] exited with code=0 in 0.834 seconds
  • linhas 9-12: falha na autenticação do utilizador: o servidor não reteve o facto de ter sido iniciada uma sessão JSON. Isto deve-se ao facto de o cookie de sessão enviado em resposta ao primeiro pedido [init-session] não ter sido reenviado;

12.8. script [axios-04]

O script [axios-04] introduz duas melhorias ao script [axios-03]:

  • ele lida com o cookie de sessão;
  • integra numa função [getRemoteData] o que é comum às funções [initSession] e [authenticateUser];

'use strict';
import axios from 'axios';
import qs from 'qs'
 
// axios configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
 
// session cookie
const sessionCookieName = "PHPSESSID";
let sessionCookie = '';
 
// init session
async function initSession(axios) {
  // 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 getRemoteData(axios, options);
}
 
async function authentifierUtilisateur(axios, 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 getRemoteData(axios, options);
}
 
async function getRemoteData(axios, options) {
  // for the session cookie
  if (!options.headers) {
    options.headers = {};
  }
  options.headers.Cookie = sessionCookie;
  // execute query HTTP
  let response;
  try {
    // asynchronous request
    response = await 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('^(' + sessionCookieName + '.+?);').exec(setCookie[i]);
      if (results) {
        // the session cookie is stored
        // eslint-disable-next-line require-atomic-updates
        sessionCookie = results[1];
        // we found
        trouvé = true;
      } else {
        // next item
        i++;
      }
    }
  }
  // the server response is in [response.data]
  return response.data;
}
 
// the main function executes asynchronous functions one by one
async function main() {
  try {
    // init-session
    console.log("action init-session en cours ---------------------------------------------");
    const response1 = await initSession(axios);
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response1);
    // authenticate-user
    console.log("action authentifier-utilisateur en cours ---------------------------------------------");
    const response2 = await authentifierUtilisateur(axios, 'admin', 'admin');
    console.log("succès ---------------------------------------------");
    console.log("réponse=", response2)
  } catch (error) {
    console.log("erreur ---------------------------------------------");
    console.log("erreur=", error.message);
  }
}
 
// test
main();

Comentários

  • linhas 14–26: a função [initSession]. Agora, ela simplesmente prepara a solicitação HTTP a ser enviada ao servidor, mas não a executa. Ela delega essa tarefa ao método [getRemoteDate] nas linhas 49–95;
  • linhas 28–47: a função [authentifierUtilisateur] segue o mesmo procedimento;
  • linha 49: a função [getRemoteData] recebe as duas informações necessárias para executar um pedido HTTP:
    • [axios], o objeto responsável por enviar a solicitação e receber a resposta;
    • [options], as opções de configuração para a solicitação a ser enviada ao servidor;
  • linha 59: execução da solicitação e espera pela resposta JSON;
  • linhas 60–68: tratamento de eventuais exceções;
  • linha 64: recuperar a resposta, que pode estar encapsulada no objeto de erro;
  • linha 67: se o servidor lançou uma exceção sem incluir a resposta do servidor, então o erro recebido é propagado para o código de chamada;
  • A função [getRemoteData] gere o cookie de sessão:
    • armazena-o na variável [sessionCookie] (linha 11) quando o recebe pela primeira vez;
    • depois devolve-o com cada novo pedido HTTP;
  • linhas 72–92: [getRemoteData] analisa cada resposta do servidor para determinar se este enviou o cabeçalho HTTP [Set-Cookie]. Sabemos que o servidor envia um cookie de sessão denominado [PHPSESSID] (linha 10). Este é, portanto, o cookie que procuramos (linha 10);
  • linha 72: recuperamos os cabeçalhos HTTP [Set-Cookie] se existirem (não distingue maiúsculas de minúsculas). Na verdade, pode haver vários cabeçalhos [Set-Cookie], pelo que recuperamos uma matriz;
  • linha 73: se tiver sido recuperada uma matriz de cookies;
  • linhas 78–90: procuramos o cookie de sessão entre todos os cookies da matriz;
  • linha 80: a expressão relacional utilizada para procurar o cookie de sessão no cookie #i;
  • linha 81: se a comparação devolveu resultados;
  • linha 84: temos em results[1], o primeiro parêntese do padrão da expressão relacional, ou seja, (PHPSESSID=xxxx) até ao parêntese de fecho (não incluído) que encerra o cookie de sessão;
  • linhas 50–54: em cada pedido, o cookie de sessão é incluído nos cabeçalhos HTTP do pedido. Na primeira vez, este cookie está vazio e, por isso, será ignorado pelo servidor;

Os resultados da execução são os seguintes:


[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-04.js"
action init-session en cours ---------------------------------------------
succès ---------------------------------------------
réponse= { action: 'init-session',
  'état': 700,
  'réponse': 'session démarrée avec type [json]' }
action authentifier-utilisateur en cours ---------------------------------------------
succès ---------------------------------------------
réponse= { action: 'authentifier-utilisateur',
  'état': 200,
  'réponse': 'Authentification réussie [admin, admin]' }
 
[Done] exited with code=0 in 0.982 seconds