Skip to content

12. Le funzioni HTTP di JavaScript

Image

12.1. Scelta di una libreria HTTP

In questo caso abbiamo scelto due librerie:

EcmaScript 6 dispone nativamente di una funzione denominata HTTP, che non è implementata da [node.js] (settembre 2019). Esiste una libreria denominata [node-fetch] che consente di utilizzare la funzione [fetch] in Node. Questa libreria utilizza alcune API specifiche di [node.js]. Un codice [node-fetch] potrebbe quindi non essere trasferibile al 100% in un ambiente non [node], ad esempio in un browser;

Esiste inoltre una libreria denominata [axios] dedicata alle richieste HTTP, compatibile sia con [node.js] che con i browser. È questa libreria che utilizzeremo alla fine.

Presenteremo lo stesso script scritto con entrambe le librerie per dimostrare che il processo di codifica è analogo.

12.2. Configurazione dell’ambiente di lavoro

12.2.1. Installazione del server di calcolo delle imposte

Infine, scriveremo un’applicazione web con la seguente architettura:

Image

JS: JavaScript

Il codice JavaScript è lato client:

  • da un servizio di pagine o frammenti statici;
  • di un servizio jSON;

Il codice JavaScript è quindi un client jSON e, in quanto tale, può essere organizzato in livelli [UI, métier, dao] (UI: Interfaccia utente) proprio come i nostri client jSON scritti in PHP.

Il server sarà quello per il calcolo delle imposte, di cui abbiamo già scritto 13 versioni. Ne scriveremo una quattordicesima. Iniziamo quindi duplicando, in NetBeans, la cartella della versione 13 nella cartella della versione 14:

Image

  • in [6], modifichiamo il file [config.json] della versione 14 nel modo seguente:

{
    "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"
}
  • alla riga 3, modifichiamo la cartella radice dell’applicazione;

Per accedere a questo server, è necessario avviare i servizi [Laragon].

Fatto ciò, possiamo testare con [Postman] (cfr. articolo al link) questa nuova versione del server, per ora identica alla versione 13. Possiamo utilizzare la raccolta di richieste utilizzata per testare la versione 12 del server di calcolo delle imposte:

Image

  • in [1-4], utilizzare la richiesta [init-session-700] per inizializzare una sessione jSON;
  • in [4-5], sostituire [version-12] con [version-14] per testare la versione 14 del progetto;
  • all'esecuzione si dovrebbe ricevere dal server la risposta jS0N [6];

La versione 14 del server è ora operativa. Dovremo apportare alcune lievi modifiche. Ricordiamo l’API di questo server:

Azione
Ruolo
Contesto di esecuzione
init-session
Serve a impostare il tipo (json, xml, html) delle risposte desiderate
Richiesta GET main.php?action=init-session&type=x
può essere inviata in qualsiasi momento
autenticazione-utente
Autorizza o meno un utente ad accedere
Richiesta POST main.php?action=autenticare-utente
La richiesta deve contenere due parametri inviati via POST [user, password]
Può essere inviata solo se il tipo di sessione (json, xml, html) è noto
calcolo-imposta
Esegue una simulazione del calcolo delle imposte
Richiesta POST main.php?action=calcolo-imposta
La richiesta deve avere tre parametri POST [marié, enfants, salaire]
Può essere inviata solo se il tipo di sessione (json, xml, html) è noto e l’utente è autenticato
lister-simulations
Richiede la visualizzazione dell’elenco delle simulazioni effettuate dall’inizio della sessione
Richiesta GET main.php?action=lister-simulations
La richiesta non accetta altri parametri
Può essere emessa solo se il tipo di sessione (json, xml, html) è noto e l'utente è autenticato
elimina-simulazione
Elimina una simulazione dall'elenco delle simulazioni
Richiesta GET main.php?action=lister-simulations&numero=x
La richiesta non accetta altri parametri
Può essere inviata solo se il tipo di sessione (json, xml, html) è noto e l'utente è autenticato
fine-sessione
Termina la sessione di simulazioni.
Tecnicamente, la vecchia sessione web viene eliminata e ne viene creata una nuova
Può essere inviata solo se il tipo di sessione (json, xml, html) è noto e l'utente è autenticato

12.2.2. Installazione delle librerie HTTP del client JavaScript

In una prima fase, lavoreremo con la seguente architettura:

Image

  • in [1], uno script da console [node.js] effettua una richiesta HTTP al server jSON per il calcolo dell’imposta;
  • in [4], riceve questa risposta e la visualizza sulla console;

Nell’esempio n. 1 utilizzeremo le librerie [node-fetch] e [axios], mentre per gli esempi successivi manterremo solo [axios]. Ora installiamo queste due librerie JavaScript dal terminale di [VSCode]:

Image

Utilizzeremo anche la libreria [qs], che consente la codifica URL di una stringa di caratteri. Ricordiamo che questa codifica viene utilizzata per codificare i parametri di una richiesta HTTP, GET o POST.

Image

12.3. script [fetch-01]

Lo script [fetch-01] utilizza la libreria [node-fetch] per inizializzare una sessione jSON con il server di calcolo delle imposte. Il suo codice è il seguente:


'use strict';

// importazioni
import fetch from 'node-fetch';
import qs from 'qs';
import { sprintf } from 'sprintf-js';
import moment from 'moment';


// URL di base del server di calcolo delle imposte
const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
// inizializzazione sessione
async function initSession() {
  // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    timeout: 2000
  };
  // esecuzione della richiesta HTTP [get /main.php?action=init-session&type=json]
  let débutFetch;
  try {
    // richiesta asincrona - [fetch] restituisce una promessa
    débutFetch = moment(Date.now());
    const response = await fetch(baseUrl + qs.stringify({
      action: 'init-session',
      type: 'json'
    }), options);
    // [response] è l'intera risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // si visualizza questa risposta per osservarne la struttura
    console.log(sprintf("réponse fetch formatée en json,=%j, %s", response, heure(débutFetch)));
    console.log("réponse fetch en javascript=", response);
    // si possono ottenere le intestazioni HTTP
    console.log("entêtes de la réponse=", response.headers);
    // se la risposta è di tipo application/json, la risposta JSON del server viene ottenuta con la funzione asincrona [response.json()]
    // in questo caso il codice chiamante ottiene un oggetto [Promise]
    // [await] consente di ottenere la risposta [json] dal server anziché la sua promessa
    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;
    // se la risposta è di tipo text/plain, la risposta testuale del server viene ottenuta con [response.text()]
    // in questo caso il codice chiamante ottiene un oggetto [Promise]
    // [await] consente di ottenere la risposta [texte] dal server anziché la sua promessa
    // const text = await response.text();
    // console.log("risposta testo=", text);
    // return text;
  } catch (error) {
    // ci troviamo qui perché il server ha inviato un codice di errore [404 Not Found, ...] accompagnato da un corpo vuoto: visualizziamo l'errore per verificarne la struttura
    // oppure perché il client [fetch] ha generato un'eccezione (rete non accessibile, ...)
    // viene visualizzata la struttura dell'errore
    console.log(sprintf("error fetch en json=%j, %s", error, heure(débutFetch)));
    console.log("error fetch en javascript=", typeof (error), error);
    // si invia il messaggio di errore ricevuto
    throw error.message;
  }
}

// la funzione main esegue la funzione asincrona [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();

// utilità per la visualizzazione dell'ora e della durata
function heure(début) {
  // ora corrente
  const now = moment(Date.now());
  // formattazione dell'ora
  let result = "heure=" + now.format("HH:mm:ss:SSS");
  // è necessario calcolare una durata?
  if (début) {
    const durée = now - début;
    const milliseconds = durée % 1000;
    const seconds = Math.floor(durée / 1000);
    // formattazione di ora e durata
    result = result + sprintf(", durée= %s seconde(s) et %s millisecondes", seconds, milliseconds);
  }
  // risultato
  return result;
}

Commenti

  • le funzioni HTTP di JavaScript sono funzioni asincrone. Qui applichiamo quanto appreso nella sezione precedente (cfr. link);
  • riga 24: per attendere che la risposta della funzione asincrona [fetch] venga pubblicata sul ciclo di eventi di [node.js], utilizziamo la parola chiave [await]. Sappiamo che questa istruzione deve trovarsi in un codice preceduto dalla parola chiave [async] (riga 13);
  • righe 13-56: incapsuliamo il codice HTTP nella funzione asincrona [initSession];
  • righe 59-69: viene utilizzata una seconda funzione asincrona [main] per chiamare in modo bloccante (async/await) la funzione asincrona [initSession];
  • riga 72: viene chiamata la funzione asincrona [main];
  • sebbene l’intero codice sembri sincrono, si tratta effettivamente di funzioni asincrone che vengono eseguite, ma in modo bloccante;
  • riga 19: per inizializzare una sessione jSON con il server di calcolo delle imposte, è necessario inviare il comando HTTP [get /main.php?action=init-session&type=json]. È ciò che fa il codice delle righe 24-27. La sintassi di [fetch] è la seguente: [fetch(URL, options)] con:
    • [URL]: l’URL interrogata;
    • [options]: un oggetto che definisce le opzioni della richiesta. È qui, in particolare, che si definiscono le intestazioni HTTP che si desidera inviare alla macchina di destinazione;
  • righe 15-18: si definiscono le opzioni della richiesta che si desidera effettuare:
    • [method]: si desidera eseguire un GET;
    • [timeout]: si desidera che il client [fetch] non attenda più di 2 secondi la risposta del server. Se tale limite viene superato, [fetch] genererà un'eccezione;
  • riga 24: per ottenere URL [/main.php?action=init-session&type=json], si utilizza la libreria [qs] per ottenere la codifica URL dei parametri [action,type] del GET. La stringa ottenuta è [init-session&type=json], che avremmo potuto costruire noi stessi. Volevamo semplicemente mostrare come ottenere una stringa codificata URL;
  • riga 24: la parola chiave [await] indica che qui viene avviata un’attività asincrona e che si attende che pubblichi la sua risposta sul ciclo di eventi di [node.js];
  • riga 24: in [response] si ottiene un oggetto complesso che descrive l’intera risposta HTTP ricevuta (intestazioni e documento);
  • righe 30-31: si visualizza l’oggetto [response] per osservarne la struttura, prima come stringa di caratteri e poi come oggetto JavaScript;
  • riga 33: si visualizzano le intestazioni HTTP inviate dal server;
  • riga 38: sappiamo che il server di calcolo delle imposte invierà una stringa jSON. Questa è incapsulata nell’oggetto [response]. È possibile ottenerla con il metodo [response.json()]. Tuttavia, questo metodo è asincrono. Si scrive quindi [await response.json()] per ottenere la stringa jSON che verrà pubblicata sul ciclo di eventi di [node.js]. In realtà non si ottiene la stringa jSON, ma l’oggetto JavaScript da essa rappresentato;
  • riga 39: visualizzazione della stringa jSON ricevuta;
  • riga 40: si restituisce l’oggetto JavaScript ricevuto;
  • riga 47: si intercetta un eventuale errore dell’istruzione [fetch]. Quest’ultima genera un’eccezione solo se l’operazione HTTP non è andata a buon fine e non è stata ricevuta alcuna risposta dal server. Se è stata ricevuta una risposta, anche con un codice HTTP diverso da [200 OK], [fetch] non genera un'eccezione e la risposta del server sarà disponibile alla riga 38;
  • righe 51-52: viene visualizzato l’oggetto [error] ricevuto dalla clausola [catch], prima come stringa jSON e poi come oggetto JavaScript;
  • riga 54: il messaggio di errore di [fetch] si trova in [error.message];
  • righe 59-69: la funzione asincrona [main] avvia la funzione asincrona [initSession] in modo bloccante (await alla riga 62);
  • riga 72: viene avviata la funzione asincrona [main] e il codice principale dello script viene quindi completato. Lo script globale terminerà quando le attività asincrone avviate avranno pubblicato i propri risultati sul ciclo di eventi;

I risultati dell’esecuzione sono i seguenti:

Caso 1: il server Laragon non è in esecuzione


[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:
   'timeout di rete all'indirizzo: 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

Commenti

  • riga 3: la richiesta HTTP fallisce dopo 2 secondi e 62 millisecondi a causa del timeout di 2 secondi che era stato imposto alla richiesta HTTP;
  • righe 4-9: l’oggetto JavaScript [error] intercettato dalla clausola [catch(error)]. Questo oggetto ha due proprietà:
    • [FetchError]: riga 4;
    • [message]: righe 10-12;
  • riga 14: il messaggio di errore ricevuto dalla funzione asincrona [main];

Caso 2: il server Laragon è in esecuzione


[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',
  'stato': 700,
  'risposta': 'sessione avviata con tipo [json]' } oggetto

[Done] exited with code=0 in 1.022 seconds

Commenti

  • riga 3: [fetch] riceve la risposta dal server dopo 375 ms;
  • righe 4-39: la struttura dell’oggetto JavaScript [response] che incapsula la risposta del server. Tra le sue proprietà, alcune potrebbero interessarci:
    • [status] (riga 25): codice HTTP della risposta del server;
    • [statusText] (riga 26): testo associato a questo codice;
    • [headers] (riga 27): le intestazioni HTTP della risposta del server;
    • [body] (riga 8): rappresenta il documento inviato dal server. L'istruzione [fetch] fornisce metodi per utilizzarlo;
  • righe 29-39: le intestazioni HTTP della risposta del server;
  • riga 40: la funzione asincrona [response.json()] ha restituito la sua risposta dopo 1 millisecondo;
  • righe 42-44: l'oggetto JavaScript ricevuto dalla funzione asincrona [main];

Caso 3: il server Laragon è in esecuzione, ma gli viene inviato un comando errato:

Image

  • sopra, alla riga 26, si passa al server un tipo di sessione errato;

I risultati dell’esecuzione sono i seguenti:


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',
  'stato': 703,
  'risposta': 'parametro type=[x] non valido' } oggetto

[Done] exited with code=0 in 0.712 seconds
  • la risposta del server viene ricevuta alla riga 2;
  • riga 24: si può notare che il codice HTTP della risposta del server è 400, un codice di errore. Tuttavia, [fetch] non ha generato alcuna eccezione. Finché [fetch] riceve una risposta dal server, la elabora e non genera un'eccezione;
  • righe 41-43: la risposta ottenuta dalla funzione asincrona [main];

12.4. script [fetch-02]

Lo script seguente riprende lo script [fetch-01] eliminando tutti i dettagli superflui:


'use strict';

// importazioni
import fetch from 'node-fetch';
import qs from 'qs';

// URL base del server di calcolo delle imposte
const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
// inizializzazione sessione
async function initSession() {
  // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    timeout: 2000
  };
  // esecuzione della richiesta HTTP [get /main.php?action=init-session&type=json]
  const response = await fetch(baseUrl + qs.stringify({
    action: 'init-session',
    type: 'json'
  }), options);
  // risultato ricevuto in jSON
  return await response.json();
}

// la funzione main esegue la funzione asincrona [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();

I risultati di un'esecuzione normale:


[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',
  'stato': 700,
  'risposta': 'sessione avviata con tipo [json]' }

[Done] exited with code=0 in 0.56 seconds

Risultati di un'esecuzione con eccezione (si arresta il server 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]

Riprendiamo qui lo script [fetch-01], che riscriviamo utilizzando la libreria [axios]. Ricordiamo che il nostro interesse per questa libreria risiede nel fatto che sia portabile dall’ambiente [node.js] a quelli dei browser più comuni. Ciò consente:

  • in una fase 1, di testare i nostri script in un ambiente [node.js];
  • nella fase 2, di portarli su un browser;

Lo script [axios-01] riprende la struttura dello script [fetch-01]:


'use strict';
import axios from 'axios';

// configurazione predefinita di axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';

// inizializzazione della sessione
async function initSession(axios) {
  // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    // parametri di URL
    params: {
      action: 'init-session',
      type: 'json'
    }
  };
  // esecuzione della richiesta HTTP [get /main.php?action=init-session&type=json]
  try {
    // richiesta asincrona
    const response = await axios.request('main.php', options);
    // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
    // si visualizza questa risposta per osservarne la struttura
    console.log("réponse axios=", response);
    // la risposta del server si trova in [response.data]
    return response.data;
  } catch (error) {
    // ci troviamo qui perché il server ha inviato un codice di errore [404 Not Found, 500 Internal Server Error, ...]
    // il parametro [error] è un'istanza di eccezione: può assumere diverse forme
    // viene visualizzato per osservarne la struttura
    console.log("axios error=", typeof (error), error);
    if (error.response) {
      // il server ha segnalato un errore nello stato HTTP, ma ha anche inviato una risposta
      // quindi questa si trova in [error.response.data]
      // si sa che il server invia risposte jSON con struttura {azione, stato, risposta}
      // e che, in caso di errore, il messaggio di errore si trova in [réponse]
      return error.response.data;
    } else {
      // viene generato l’errore
      throw error;
    }
  }
}

// la funzione main esegue la funzione asincrona [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();

Commenti

  • riga 2: si importa la libreria [axios];
  • righe 5-6: configurazione predefinita delle richieste HTTP. Le opzioni [axios.defaults] sono valide per tutte le richieste HTTP emesse dall’oggetto [axios] senza che sia necessario richiamarle ad ogni nuova richiesta;
  • riga 5: timeout di 2 secondi per tutte le richieste;
  • riga 6: tutte le URL saranno espresse in relazione alla URL di base;
  • riga 9: la funzione asincrona [initSession];
  • righe 11-18: le opzioni della richiesta HTTP che verrà inviata (oltre alle opzioni predefinite già definite alle righe 5-6);
  • righe 14-17: i parametri di URL [action=init-session&type=json]. L’oggetto [params] verrà automaticamente convertito in una stringa codificata URL;
  • riga 22: chiamata bloccante della funzione asincrona [axios.request]. Il primo parametro è l’oggetto di destinazione URL, costruito sommando [main.php] all’oggetto di base URL definito alla riga 6. Il secondo parametro è l’oggetto [options] delle righe 11-18;
  • riga 25: [response] è un oggetto JavaScript che incapsula l’intera risposta HTTP del server (intestazioni HTTP + documento di risposta). Viene visualizzato per osservarne la struttura JavaScript;
  • riga 27: se il server ha inviato un documento, questo si trova in [response.data]. In questo caso sappiamo che il server invia una risposta jSON accompagnata dall’intestazione HTTP [Content-type : application/json]. La presenza di questa intestazione fa sì che [axios] deserializzi automaticamente [response.data] in un oggetto JavaScript;
  • riga 28: la funzione [axios] può generare un'eccezione. È qui che [axios] differisce da [fetch]. Viene generata un'eccezione nei seguenti casi:
    • non è stato possibile inviare la richiesta HTTP (errore lato client);
    • il server ha restituito un codice di errore HTTP (400, 404, 500, …) (questo parametro è infatti configurabile). Si ricorda che, se questo codice HTTP è accompagnato da una risposta, [fetch] non generava un'eccezione, mentre [axios] ne genera una. Tuttavia, se il codice di errore HTTP è accompagnato da un documento, quest’ultimo viene inserito in [error.response];
  • riga 32: viene visualizzata la struttura JavaScript dell’oggetto [error];
  • righe 33-38: se l’oggetto [error] contiene un oggetto [response], allora è questa la risposta che viene restituita al codice chiamante;
  • righe 39-42: negli altri casi, si restituisce l'oggetto [error] al codice chiamante;
  • righe 47-57: la funzione asincrona [main];
  • riga 50: chiamata bloccante alla funzione asincrona [initSession]. Si recupera la risposta jSON dal server come oggetto JavaScript;
  • righe 53-56: intercettazione di un eventuale errore. Il messaggio di errore si trova in [error.message];

I risultati dell’esecuzione sono i seguenti:

Caso 1: il server Laragon non è in esecuzione


[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

Commenti

  • righe 33-136: l'oggetto [error] contiene numerose informazioni;
    • [Error], righe 3-9: una descrizione dell’errore verificatosi;
    • [config], righe 10-27: la configurazione della richiesta HTTP che ha portato a questo errore;
    • [config.url], righe 11-12: il URL di destinazione;
    • [config.method], riga 13: metodo della richiesta;
    • [config.params], riga 14: i parametri di URL;
    • [config.headers], righe 16-17: le intestazioni HTTP della richiesta;
    • [config.baseURL], riga 18: l’URL di base dell’URL di destinazione;
    • [config.timeout], riga 21: il timeout della richiesta;
    • [code], riga 28: un codice di errore;
    • [request], righe 29-133: una descrizione dettagliata della richiesta HTTP. Si noti che la maggior parte delle proprietà è preceduta dal trattino basso _, a indicare che si tratta di proprietà interne all’oggetto [request] non destinate ad essere utilizzate direttamente dallo sviluppatore;
    • [response], riga 134: la risposta del server, in questo caso inesistente;
  • riga 138: il messaggio di errore visualizzato dalla funzione [main];

Caso 2: il server Laragon è in esecuzione


[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',
     'stato': 700,
     'risposta': 'sessione avviata con tipo [json]' } }
succès ---------------------------------------------
réponse= { action: 'init-session',
  'stato': 700,
  'risposta': 'sessione avviata con tipo [json]' } oggetto

[Done] exited with code=0 in 1.115 seconds

Commenti

  • righe 3-203: l’oggetto JavaScript [response] che incapsula la risposta HTTP del server;
  • riga 3, [status]: il codice HTTP della risposta;
  • riga 4, [statusText]: il testo associato al codice HTTP precedente;
  • righe 5-13, [headers]: le intestazioni HTTP della risposta:
    • riga 10, [Set-Cookie]: il cookie di sessione;
    • riga 13, [Content-Type]: il tipo di documento inviato dal server;
  • righe 14-31, [config]: la configurazione della richiesta HTTP inviata;
  • righe 32-199, [request]: l’oggetto JavaScript che descrive in dettaglio la richiesta HTTP inviata;
  • righe 200-203, [request.data]: l'oggetto JavaScript che incapsula la risposta jSON del server;
  • righe 205-207: la risposta recuperata dalla funzione asincrona [main];

Caso 3: si effettua una richiesta errata [init-session] al server Laragon;

Image

I risultati dell’esecuzione sono i seguenti:


[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',
        'stato': 703,
        'risposta': 'parametro type=[x] non valido' } },
  isAxiosError: true,
  toJSON: [Function] }
succès ---------------------------------------------
réponse= { action: 'init-session',
  'stato': 703,
  'risposta': 'parametro type=[x] non valido' } oggetto

[Done] exited with code=0 in 0.69 seconds

Poiché il server ha risposto con un codice HTTP 400 (riga 15), [axios] ha generato un'eccezione (anche in questo caso il comportamento è configurabile). Sebbene [axios] abbia generato un'eccezione, si ottiene comunque la risposta HTTP dal server in [error.response] (riga 14) e il documento jSON inviato a [error.response.data] (riga 34). Righe 41-43: la funzione [main] recupera correttamente la risposta jSON dal server.

12.6. script [axios-02]

Ora che abbiamo descritto in dettaglio gli oggetti gestiti dalla libreria [axios] durante una richiesta HTTP, possiamo riscrivere lo script [axios-01] nel modo seguente:


'use strict';
import axios from 'axios';

// configurazione predefinita di axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';

// inizializzazione della sessione
async function initSession(axios) {
  // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    // parametri di URL
    params: {
      action: 'init-session',
      type: 'json'
    }
  };
  try {
    // esecuzione della richiesta HTTP [get /main.php?action=init-session&type=json]
    const response = await axios.request('main.php', options);
    // la risposta del server è contenuta in [response.data]
    return response.data;
  } catch (error) {
    // risposta del server
    if (error.response) {
      // la risposta jSON si trova in [error.response.data]
      return error.response.data;
    } else {
      // l'errore viene generato nuovamente
      throw error;
    }
  }
}

// la funzione main esegue la funzione asincrona [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]

Lo script [axios-03] riprende la metodologia dello script [axios-02]. Questa volta si aggiunge la richiesta HTTP [authentifier-utilisateur] al server, che viene effettuata tramite un POST:


'use strict';
import axios from 'axios';
import qs from 'qs'

// configurazione di Axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/versione-14';


// inizializzazione sessione
async function initSession(axios) {
  // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    // parametri di URL
    params: {
      action: 'init-session',
      type: 'json'
    }
  };
  try {
    // esecuzione della richiesta HTTP [get /main.php?action=init-session&type=json]
    const response = await axios.request('main.php', options);
    // la risposta del server è contenuta in [response.data]
    return response.data;
  } catch (error) {
    // risposta del server
    if (error.response) {
      // la risposta jSON si trova in [error.response.data]
      return error.response.data;
    } else {
      // l'errore viene generato nuovamente
      throw error;
    }
  }
}

async function authentifierUtilisateur(axios, user, password) {
  // opzioni della richiesta HHTP [POST /main.php?action=authentifier-utilisateur]
  const options = {
    method: "POST",
    headers: {
      'Content-type': 'application/x-www-form-urlencoded',
    },
    // corpo del POST
    data: qs.stringify({
      user: user,
      password: password
    }),
    // parametri dell'URL
    params: {
      action: 'authentifier-utilisateur'
    }
  };
  try {
    // esecuzione della richiesta HTTP [post /main.php?action=authentifier-utilisateur]
    const response = await axios.request('main.php', options);
    // la risposta del server si trova in [response.data]
    return response.data;
  } catch (error) {
    // risposta del server
    if (error.response) {
      // la risposta jSON si trova in [error.response.data]
      return error.response.data;
    } else {
      // l'errore viene generato nuovamente
      throw error;
    }
  }
}

// la funzione main esegue le funzioni asincrone una alla volta
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);
    // autenticazione-utente
    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();

Commenti

  • righe 38-70: la funzione asincrona [authentifierUtilisateur];
  • riga 39: è necessario eseguire la richiesta [POST /main.php?action=authentifier-utilisateur];
  • righe 40-54: le opzioni della richiesta HTTP;
  • riga 41: si tratta di un POST;
  • righe 42-44: i parametri di POST saranno codificati in un documento che il cliente invia insieme alla sua richiesta;
  • righe 46-49: la proprietà [data] deve contenere la stringa codificata di POST URL. A tal fine, qui si utilizza la libreria [qs] importata alla riga 3;
  • righe 55-69: per l’esecuzione della query, si ritrova lo stesso codice presente nel metodo [initSession];
  • righe 73-89: il metodo [asynchrone] chiama in successione, in modo bloccante, i metodi [initSession, authentifierUtilisateur], righe 77 e 82;
  • riga 82: si utilizza la coppia (admin, admin) come credenziali di accesso. È noto che queste vengono riconosciute dal server;

I risultati dell’esecuzione sono i seguenti:


[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',
  'stato': 700,
  'risposta': 'sessione avviata con tipo [json]' }
action authentifier-utilisateur en cours ---------------------------------------------
succès ---------------------------------------------
réponse= { action: 'authentifier-utilisateur',
  'stato': 103,
  'risposta':
   [ 'pas de session en cours. Commencer par action [init-session]' ] }

[Done] exited with code=0 in 0.834 seconds
  • righe 9-12: l’autenticazione dell’utente fallisce: il server non ha registrato il fatto che fosse stata avviata una sessione jSON. Ciò è dovuto al fatto che non è stato rinviato il cookie di sessione inviato in risposta alla prima richiesta [init-session];

12.8. script [axios-04]

Lo script [axios-04] apporta due miglioramenti allo script [axios-03]:

  • gestisce il cookie di sessione;
  • fattorizza in una funzione [getRemoteData] ciò che è comune alle funzioni [initSession] e [authentifierUtilisateur];

'use strict';
import axios from 'axios';
import qs from 'qs'

// configurazione di Axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';

// cookie di sessione
const sessionCookieName = "PHPSESSID";
let sessionCookie = '';

// inizializzazione sessione
async function initSession(axios) {
  // opzioni della richiesta HHTP [get /main.php?action=init-session&type=json]
  const options = {
    method: "GET",
    // parametri di URL
    params: {
      action: 'init-session',
      type: 'json'
    }
  };
  // esecuzione della query HTTP
  return await getRemoteData(axios, options);
}

async function authentifierUtilisateur(axios, user, password) {
  // opzioni della richiesta HHTP [post /main.php?action=authentifier-utilisateur]
  const options = {
    method: "POST",
    headers: {
      'Content-type': 'application/x-www-form-urlencoded',
    },
    // corpo del POST
    data: qs.stringify({
      user: user,
      password: password
    }),
    // parametri dell'URL
    params: {
      action: 'authentifier-utilisateur'
    }
  };
  // esecuzione della richiesta HTTP
  return await getRemoteData(axios, options);
}

async function getRemoteData(axios, options) {
  // per il cookie di sessione
  if (!options.headers) {
    options.headers = {};
  }
  options.headers.Cookie = sessionCookie;
  // esecuzione della richiesta HTTP
  let response;
  try {
    // richiesta asincrona
    response = await axios.request('main.php', options);
  } catch (error) {
    // il parametro [error] è un'istanza di eccezione - può assumere diverse forme
    if (error.response) {
      // la risposta del server è contenuta in [error.response]
      response = error.response;
    } else {
      // l'errore viene generato nuovamente
      throw error;
    }
  }
  // la risposta è l'insieme della risposta HTTP del server (intestazioni HTTP + la risposta stessa)
  // si recupera il cookie di sessione, se presente
  const setCookie = response.headers['set-cookie'];
  if (setCookie) {
    // setCookie è un array
    // si cerca il cookie di sessione in questo array
    let trouvé = false;
    let i = 0;
    while (!trouvé && i < setCookie.length) {
      // si cerca il cookie di sessione
      const results = RegExp('^(' + sessionCookieName + '.+?);').exec(setCookie[i]);
      if (results) {
        // si memorizza il cookie di sessione
        // eslint-disable-next-line require-atomic-updates
        sessionCookie = results[1];
        // trovato
        trouvé = true;
      } else {
        // elemento successivo
        i++;
      }
    }
  }
  // la risposta del server è in [response.data]
  return response.data;
}

// la funzione main esegue le funzioni asincrone una alla volta
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);
    // autenticazione-utente
    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();

Commenti

  • righe 14-26: la funzione [initSession]. Ora si limita a preparare la richiesta HTTP da inviare al server, ma non la esegue. Affida questo compito al metodo [getRemoteDate] delle righe 49-95;
  • righe 28-47: la funzione [authentifierUtilisateur] segue lo stesso procedimento;
  • riga 49: la funzione [getRemoteData] riceve le due informazioni che le consentono di eseguire una richiesta HTTP:
    • [axios], l’oggetto che si occuperà di inviare la richiesta e di ricevere la risposta;
    • [options], le opzioni di configurazione della richiesta da inviare al server;
  • riga 59: esecuzione della richiesta e attesa bloccante della risposta jSON;
  • righe 60-68: gestione dell'eventuale eccezione;
  • riga 64: si recupera la risposta, che può essere incapsulata nell’oggetto di errore;
  • riga 67: se il server ha generato un'eccezione senza includervi la risposta del server, l'errore ricevuto viene segnalato al codice chiamante;
  • la funzione [getRemoteData] gestisce il cookie di sessione:
    • lo memorizza nella variabile [sessionCookie] (riga 11) quando lo riceve per la prima volta;
    • lo rinvia poi a ogni nuova richiesta HTTP;
  • righe 72-92: [getRemoteData] analizza ogni risposta del server per verificare se ha inviato l’intestazione HTTP [Set-Cookie]. Si sa che il server invia un cookie di sessione denominato [PHPSESSID] (riga 10). È quindi questo il cookie che si cerca (riga 10);
  • riga 72: si recuperano le intestazioni HTTP e [Set-Cookie], se presenti (la maiuscola/minuscola non ha importanza). Possono infatti esserci più intestazioni [Set-Cookie] e quindi si recupera un array;
  • riga 73: se è stato recuperato un array di cookie;
  • righe 78-90: si cerca il cookie di sessione tra tutti i cookie dell’array;
  • riga 80: l’espressione relazionale che consente di cercare il cookie di sessione nel cookie n. i;
  • riga 81: se il confronto ha restituito dei risultati;
  • riga 84: in results[1] si ha la prima parentesi del modello dell’espressione relazionale, ovvero (PHPSESSID=xxxx) fino al ; (non incluso) che conclude il cookie di sessione;
  • righe 50-54: ad ogni richiesta, il cookie di sessione è incluso nelle intestazioni HTTP della richiesta. La prima volta, questo cookie è vuoto e verrà quindi ignorato dal server;

I risultati dell’esecuzione sono i seguenti:


[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',
  'stato': 700,
  'risposta': 'sessione avviata con tipo [json]' }
action authentifier-utilisateur en cours ---------------------------------------------
succès ---------------------------------------------
réponse= { action: 'authentifier-utilisateur',
  'stato': 200,
  'risposta': 'Autenticazione riuscita [admin, admin]' }

[Done] exited with code=0 in 0.982 seconds