Skip to content

12. JavaScript-HTTP-Funktionen

Image

12.1. Auswahl einer HTTP-Bibliothek

Wir haben hier zwei Bibliotheken ausgewählt:

ECMAScript 6 verfügt nativ über eine HTTP-Funktion namens [fetch], die von [node.js] (Stand: September 2019) nicht implementiert ist. Es gibt eine Bibliothek namens [node-fetch], die es ermöglicht, die [fetch]-Funktion in Node zu verwenden. Diese Bibliothek nutzt bestimmte APIs, die spezifisch für [node.js] sind. [node-fetch]-Code ist daher möglicherweise nicht zu 100 % auf eine Nicht-[node]-Umgebung, wie beispielsweise einen Browser, übertragbar;

Es gibt auch eine Bibliothek namens [axios], die speziell für HTTP-Anfragen entwickelt wurde und sowohl mit [node.js] als auch mit Browsern kompatibel ist. Dies ist die Bibliothek, die wir letztendlich verwenden werden.

Wir werden dasselbe Skript vorstellen, das mit beiden Bibliotheken geschrieben wurde, um zu zeigen, dass der Programmieransatz ähnlich ist.

12.2. Einrichten einer Entwicklungsumgebung

12.2.1. Installation des Steuerberechnungsservers

Letztendlich werden wir eine Webanwendung mit der folgenden Architektur schreiben:

Image

JS: JavaScript

Der JavaScript-Code ist clientseitig:

  • von einem Dienst, der statische Seiten oder Fragmente bereitstellt;
  • einem JSON-Dienst;

Der JavaScript-Code ist daher ein JSON-Client und kann als solcher in Schichten [UI, Geschäftslogik, DAO] (UI: Benutzeroberfläche) organisiert werden, genau wie unsere in PHP geschriebenen JSON-Clients.

Der Server wird der Steuerberechnungsserver sein, für den wir bereits 13 Versionen geschrieben haben. Wir werden eine 14. Version schreiben. Wir beginnen daher damit, in NetBeans den Ordner „Version 13“ in den Ordner „Version 14“ zu duplizieren:

Image

  • In [6] ändern wir die Datei [config.json] der Version 14 wie folgt:

{
    "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"
}
  • In Zeile 3 ändern wir das Stammverzeichnis der Anwendung;

Um auf diesen Server zuzugreifen, müssen Sie die [Laragon]-Dienste starten.

Sobald dies erledigt ist, können wir diese neue Version des Servers – die derzeit mit Version 13 identisch ist – mithilfe von [Postman] (siehe verlinkten Artikel) testen. Wir können die Sammlung von Anfragen verwenden, mit der wir Version 12 des Steuerberechnungsservers getestet haben:

Image

  • Verwenden Sie in [1-4] die Anfrage [init-session-700], um eine JSON-Sitzung zu initialisieren;
  • ersetzen Sie in [4-5] [version-12] durch [version-14], um Version 14 des Projekts zu testen;
  • Bei der Ausführung sollten wir die JSON-Antwort [6] vom Server erhalten;

Version 14 des Servers ist nun betriebsbereit. Wir müssen sie leicht anpassen. Sehen wir uns die API dieses Servers an:

Aktion
Rolle
Ausführungskontext
init-session
Wird verwendet, um den Typ (json, xml, html) der gewünschten Antworten festzulegen
GET-Anfrage main.php?action=init-session&type=x
kann jederzeit gesendet werden
authenticate-user
Autorisiert oder verweigert die Anmeldung eines Benutzers
POST-Anfrage main.php?action=authenticate-user
Die Anfrage muss zwei übermittelte Parameter enthalten [user, password]
Kann nur gesendet werden, wenn der Sitzungstyp (json, xml, html) bekannt ist
calculate-tax
Führt eine Simulation der Steuerberechnung durch
POST-Anfrage an main.php?action=calculate-tax
Die Anfrage muss drei übermittelte Parameter enthalten [married, children, salary]
Kann nur ausgeführt werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist
list-simulations
Anfrage zum Anzeigen der Liste der seit Beginn der Sitzung durchgeführten Simulationen
GET-Anfrage an main.php?action=list-simulations
Die Anfrage akzeptiert keine weiteren Parameter
Kann nur ausgegeben werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist
delete-simulation
Löscht eine Simulation aus der Liste der Simulationen
GET-Anfrage main.php?action=list-simulations&number=x
Die Anfrage akzeptiert keine weiteren Parameter
Kann nur ausgeführt werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist
end-session
Beendet die Simulationssitzung.
Technisch gesehen wird die alte Web-Sitzung gelöscht und eine neue Sitzung erstellt
Kann nur ausgegeben werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist

12.2.2. Installation der JavaScript-Client-HTTP-Bibliotheken

Zunächst arbeiten wir mit der folgenden Architektur:

Image

  • In [1] sendet ein Konsolenskript [node.js] eine HTTP-Anfrage an den JSON-Server für die Steuerberechnung;
  • In [4] empfängt es diese Antwort und zeigt sie in der Konsole an;

In Beispiel 1 verwenden wir die Bibliotheken [node-fetch] und [axios], und für die folgenden Beispiele behalten wir dann nur [axios] bei. Wir installieren nun diese beiden JavaScript-Bibliotheken über das [VSCode]-Terminal:

Image

Wir werden außerdem die Bibliothek [qs] verwenden, die die URL-Kodierung einer Zeichenkette ermöglicht. Zur Erinnerung: Diese Kodierung wird verwendet, um die Parameter einer HTTP-GET- oder -POST-Anfrage zu kodieren.

Image

12.3. Skript [fetch-01]

Das Skript [fetch-01] verwendet die Bibliothek [node-fetch], um eine JSON-Sitzung mit dem Steuerberechnungsserver zu initialisieren. Der Code lautet wie folgt:


'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;
}

Kommentare

  • JavaScript-HTTP-Funktionen sind asynchrone Funktionen. Hier wenden wir das an, was wir im vorherigen Abschnitt gelernt haben (siehe Link);
  • Zeile 24: Um darauf zu warten, dass die Antwort der asynchronen Funktion [fetch] in der [node.js]-Ereignisschleife veröffentlicht wird, verwenden wir das Schlüsselwort [await]. Wir wissen, dass diese Anweisung innerhalb von Code stehen muss, dem das Schlüsselwort [async] vorangestellt ist (Zeile 13);
  • Zeilen 13–56: Wir kapseln den HTTP-Code in die asynchrone Funktion [initSession] ein;
  • Zeilen 59–69: Eine zweite asynchrone Funktion [main] wird verwendet, um die asynchrone Funktion [initSession] blockierend aufzurufen (async/await);
  • Zeile 72: Die asynchrone Funktion [main] wird aufgerufen;
  • obwohl der gesamte Code synchronem Code ähnelt, handelt es sich tatsächlich um asynchrone Funktionen, die jedoch blockierend ausgeführt werden;
  • Zeile 19: Um eine JSON-Sitzung mit dem Steuerberechnungsserver zu initialisieren, müssen Sie ihm die HTTP-Anfrage [get /main.php?action=init-session&type=json] senden. Genau das tut der Code in den Zeilen 24–27. Die Syntax für [fetch] lautet wie folgt: [fetch(URL, options)] mit:
    • [URL]: die abzufragende URL;
    • [options]: ein Objekt, das die Anfrageoptionen definiert. Hier definieren wir die HTTP-Header, die wir an den Zielserver senden möchten;
  • Zeilen 15–18: Wir definieren die Optionen für die Anfrage, die wir stellen möchten:
    • [method]: Wir möchten einen GET-Aufruf ausführen;
    • [timeout]: Der [fetch]-Client soll nicht länger als 2 Sekunden auf die Antwort des Servers warten. Wird diese Zeitüberschreitung überschritten, löst [fetch] eine Ausnahme aus;
  • Zeile 24: Um die URL [/main.php?action=init-session&type=json] zu erhalten, verwenden wir die [qs]-Bibliothek, um die URL-kodierten Parameter [action,type] der GET-Anfrage abzurufen. Die resultierende Zeichenkette lautet [init-session&type=json], die wir auch selbst hätten erstellen können. Wir wollten lediglich demonstrieren, wie man eine URL-kodierte Zeichenkette erhält;
  • Zeile 24: Das Schlüsselwort [await] zeigt an, dass hier eine asynchrone Aufgabe gestartet wird und dass wir darauf warten, dass sie ihre Antwort in der [node.js]-Ereignisschleife veröffentlicht;
  • Zeile 24: In [response] erhalten wir ein komplexes Objekt, das die gesamte empfangene HTTP-Antwort (Header und Body) beschreibt;
  • Zeilen 30–31: Wir geben das [response]-Objekt aus, um seine Struktur zu sehen, zuerst als Zeichenkette und dann als JavaScript-Objekt;
  • Zeile 33: Wir zeigen die vom Server gesendeten HTTP-Header an;
  • Zeile 38: Wir wissen, dass der Steuerberechnungsserver eine JSON-Zeichenkette senden wird. Diese Zeichenkette ist im [response]-Objekt gekapselt. Wir können sie mit der Methode [response.json()] abrufen. Diese Methode ist jedoch asynchron. Wir schreiben daher [await response.json()], um die JSON-Zeichenkette abzurufen, die in der [node.js]-Ereignisschleife veröffentlicht wird. Tatsächlich erhalten wir nicht die JSON-Zeichenkette selbst, sondern das JavaScript-Objekt, das durch sie repräsentiert wird;
  • Zeile 39: Anzeige der empfangenen JSON-Zeichenkette;
  • Zeile 40: Gibt das empfangene JavaScript-Objekt zurück;
  • Zeile 47: Wir fangen mögliche Fehler aus der [fetch]-Anweisung ab. Diese Anweisung löst nur dann eine Ausnahme aus, wenn der HTTP-Vorgang fehlgeschlagen ist und keine Antwort vom Server empfangen wurde. Wenn eine Antwort empfangen wurde, löst [fetch] auch bei einem anderen HTTP-Statuscode als [200 OK] keine Ausnahme aus, und die Serverantwort ist in Zeile 38 verfügbar;
  • Zeilen 51–52: Das von der [catch]-Klausel empfangene [error]-Objekt wird angezeigt, zunächst als JSON-Zeichenkette und anschließend als JavaScript-Objekt;
  • Zeile 54: Die Fehlermeldung von [fetch] wird in [error.message] gespeichert;
  • Zeilen 59–69: Die asynchrone Funktion [main] ruft die asynchrone Funktion [initSession] blockierend auf (await in Zeile 62);
  • Zeile 72: Die asynchrone Funktion [main] wird gestartet, und der Hauptskriptcode ist damit abgeschlossen. Das gesamte Skript ist beendet, wenn die gestarteten asynchronen Aufgaben ihre Ergebnisse an die Ereignisschleife übermittelt haben;

Die Ergebnisse der Ausführung lauten wie folgt:

Fall 1: Der Laragon-Server läuft nicht


[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

Kommentare

  • Zeile 3: Die HTTP-Anfrage schlägt nach 2 Sekunden und 62 Millisekunden fehl, da für die HTTP-Anfrage ein Timeout von 2 Sekunden festgelegt wurde;
  • Zeilen 4–9: das JavaScript-[error]-Objekt, das von der [catch(error)]-Klausel abgefangen wurde. Dieses Objekt hat zwei Eigenschaften:
    • [FetchError]: Zeile 4;
    • [message]: Zeilen 10–12;
  • Zeile 14: Die von der asynchronen Funktion [main] empfangene Fehlermeldung;

Fall 2: Der Laragon-Server läuft


[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

Kommentare

  • Zeile 3: [fetch] empfängt die Serverantwort nach 375 ms;
  • Zeilen 4–39: Die Struktur des JavaScript-Objekts [response], das die Serverantwort kapselt. Unter seinen Eigenschaften könnten einige für uns von Interesse sein:
    • [status] (Zeile 25): HTTP-Statuscode der Serverantwort;
    • [statusText] (Zeile 26): Text, der diesem Code zugeordnet ist;
    • [headers] (Zeile 27): die HTTP-Header der Serverantwort;
    • [body] (Zeile 8): stellt das vom Server gesendete Dokument dar. Die Anweisung [fetch] bietet Methoden, um damit zu arbeiten;
  • Zeilen 29–39: die HTTP-Header der Serverantwort;
  • Zeile 40: Die asynchrone Funktion [response.json()] gab ihre Antwort nach 1 Millisekunde zurück;
  • Zeilen 42–44: das von der asynchronen Funktion [main] empfangene JavaScript-Objekt;

Fall 3: Der Laragon-Server läuft, aber es wird ein falscher Befehl an ihn gesendet:

Image

  • oben, Zeile 26, wird ein falscher Sitzungstyp an den Server übergeben;

Die Ergebnisse der Ausführung lauten wie folgt:


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
  • Die Serverantwort wird in Zeile 2 empfangen;
  • Zeile 24: Wir sehen, dass der HTTP-Statuscode der Serverantwort 400 ist, ein Fehlercode. [fetch] hat jedoch keine Ausnahme ausgelöst. Solange [fetch] eine Antwort vom Server erhält, verarbeitet es diese und löst keine Ausnahme aus;
  • Zeilen 41–43: die von der asynchronen Funktion [main] erhaltene Antwort;

12.4. Skript [fetch-02]

Das folgende Skript verwendet das Skript [fetch-01] wieder und entfernt dabei alle unnötigen Details:


'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();

Ergebnisse einer normalen Ausführung:


[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

Ergebnisse einer Ausführung mit einer Ausnahme (Beenden des Laragon-Servers):


[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. Skript [axios-01]

Hier greifen wir das Skript [fetch-01] wieder auf, das wir mithilfe der Bibliothek [axios] umschreiben. Zur Erinnerung: Unser Interesse an dieser Bibliothek liegt in ihrer Portabilität zwischen der [node.js]-Umgebung und gängigen Browsern. Dies ermöglicht:

  • In Phase 1 unsere Skripte in einer [Node.js]-Umgebung zu testen;
  • in Phase 2, sie auf einen Browser zu portieren;

Das Skript [axios-01] folgt der Struktur des Skripts [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();

Kommentare

  • Zeile 2: Wir importieren die [axios]-Bibliothek;
  • Zeilen 5–6: Standardkonfiguration für HTTP-Anfragen. Die [axios.defaults]-Optionen gelten für alle vom [axios]-Objekt gesendeten HTTP-Anfragen, ohne dass sie für jede neue Anfrage separat angegeben werden müssen;
  • Zeile 5: 2-Sekunden-Timeout für alle Anfragen;
  • Zeile 6: Alle URLs beziehen sich auf die Basis-URL;
  • Zeile 9: die asynchrone Funktion [initSession];
  • Zeilen 11–18: die Optionen für die zu sendende HTTP-Anfrage (zusätzlich zu den bereits in den Zeilen 5–6 definierten Standardoptionen);
  • Zeilen 14–17: die URL-Parameter [action=init-session&type=json]. Das [params]-Objekt wird automatisch in eine URL-kodierte Zeichenkette umgewandelt;
  • Zeile 22: blockierender Aufruf der asynchronen Funktion [axios.request]. Der erste Parameter ist die Ziel-URL, die als [main.php] an die in Zeile 6 definierte Basis-URL angehängt wird. Der zweite Parameter ist das [options]-Objekt aus den Zeilen 11–18;
  • Zeile 25: [response] ist ein JavaScript-Objekt, das die gesamte HTTP-Antwort vom Server (HTTP-Header + Antworttext) kapselt. Wir zeigen es an, um seine JavaScript-Struktur zu sehen;
  • Zeile 27: Wenn der Server ein Dokument gesendet hat, befindet es sich in [response.data]. Hier wissen wir, dass der Server eine JSON-Antwort sendet, begleitet vom HTTP-Header [Content-type: application/json]. Das Vorhandensein dieses Headers bewirkt, dass [axios] [response.data] automatisch in ein JavaScript-Objekt deserialisiert;
  • Zeile 28: Die Funktion [axios] kann eine Ausnahme auslösen. Hier unterscheidet sich [axios] von [fetch]. Eine Ausnahme wird in den folgenden Fällen ausgelöst:
    • Die HTTP-Anfrage konnte nicht gesendet werden (clientseitiger Fehler);
    • der Server hat einen HTTP-Fehlercode (400, 404, 500, …) gesendet (dieses Verhalten ist tatsächlich konfigurierbar). Beachten Sie, dass [fetch] keine Ausnahme ausgelöst hat, wenn dieser HTTP-Statuscode von einer Antwort begleitet wird, während [axios] dies tut. Wenn der HTTP-Fehlercode jedoch von einem Dokument begleitet wird, wird dieses in [error.response] abgelegt;
  • Zeile 32: Wir zeigen die JavaScript-Struktur des [error]-Objekts an;
  • Zeilen 33–38: Wenn das [error]-Objekt ein [response]-Objekt enthält, wird diese Antwort an den aufrufenden Code zurückgegeben;
  • Zeilen 39–42: In allen anderen Fällen wird das [error]-Objekt an den aufrufenden Code zurückgegeben;
  • Zeilen 47–57: die asynchrone Funktion [main];
  • Zeile 50: blockierender Aufruf der asynchronen Funktion [initSession]. Die JSON-Antwort vom Server wird als JavaScript-Objekt abgerufen;
  • Zeilen 53–56: Behandlung etwaiger Fehler. Die Fehlermeldung befindet sich in [error.message];

Die Ergebnisse der Ausführung lauten wie folgt:

Fall 1: Der Laragon-Server läuft nicht


[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

Kommentare

  • Zeilen 33–136: Das [Fehler]-Objekt enthält viele Informationen;
    • [Error], Zeilen 3–9: eine Beschreibung des aufgetretenen Fehlers;
    • [config], Zeilen 10–27: die Konfiguration der HTTP-Anfrage, die zu diesem Fehler geführt hat;
    • [config.url], Zeilen 11–12: die Ziel-URL;
    • [config.method], Zeile 13: Anfragemethode;
    • [config.params], Zeile 14: die URL-Parameter;
    • [config.headers], Zeilen 16–17: die HTTP-Header der Anfrage;
    • [config.baseURL], Zeile 18: die Basis-URL der Ziel-URL;
    • [config.timeout], Zeile 21: das Zeitlimit für die Anfrage;
    • [code], Zeile 28: ein Fehlercode;
    • [request], Zeilen 29–133: eine detaillierte Beschreibung der HTTP-Anfrage. Beachten Sie, dass den meisten Eigenschaften ein Unterstrich (_) vorangestellt ist, was darauf hinweist, dass es sich um interne Eigenschaften des [request]-Objekts handelt, die nicht für die direkte Verwendung durch den Entwickler vorgesehen sind;
    • [response], Zeile 134: die Serverantwort, die hier leer ist;
  • Zeile 138: die von der Funktion [main] angezeigte Fehlermeldung;

Fall 2: Der Laragon-Server läuft


[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

Kommentare

  • Zeilen 3–203: das JavaScript-Objekt [response], das die HTTP-Antwort des Servers kapselt;
  • Zeile 3, [status]: der HTTP-Statuscode der Antwort;
  • Zeile 4, [statusText]: der Text, der dem vorherigen HTTP-Statuscode zugeordnet ist;
  • Zeilen 5–13, [headers]: die HTTP-Header der Antwort:
    • Zeile 10, [Set-Cookie]: das Sitzungscookie;
    • Zeile 13, [Content-Type]: der Typ des vom Server gesendeten Dokuments;
  • Zeilen 14–31, [config]: die Konfiguration der gesendeten HTTP-Anfrage;
  • Zeilen 32–199, [request]: das JavaScript-Objekt, das die gesendete HTTP-Anfrage detailliert beschreibt;
  • Zeilen 200–203, [request.data]: das JavaScript-Objekt, das die JSON-Antwort des Servers kapselt;
  • Zeilen 205–207: die von der asynchronen Funktion [main] abgerufene Antwort;

Fall 3: Eine fehlerhafte [init-session]-Anfrage wird an den Laragon-Server gesendet;

Image

Die Ausführungsergebnisse lauten wie folgt:


[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

Da der Server mit einem HTTP-400-Code geantwortet hat (Zeile 15), hat [axios] eine Ausnahme ausgelöst (auch dieses Verhalten ist konfigurierbar). Obwohl [axios] eine Ausnahme ausgelöst hat, erhalten wir dennoch die HTTP-Antwort vom Server in [error.response] (Zeile 14) und das gesendete JSON-Dokument in [error.response.data] (Zeile 34). Zeilen 41–43: Die Funktion [main] ruft die JSON-Antwort korrekt vom Server ab.

12.6. Skript [axios-02]

Nachdem wir nun die Objekte beschrieben haben, die von der [axios]-Bibliothek während einer HTTP-Anfrage verarbeitet werden, können wir das Skript [axios-01] wie folgt umschreiben:


'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. Skript [axios-03]

Das Skript [axios-03] folgt derselben Methodik wie das Skript [axios-02]. Diesmal fügen wir die HTTP-Anfrage [authenticate-user] zum Server hinzu, die mittels einer POST-Anfrage gestellt wird:


'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();

Kommentare

  • Zeilen 38–70: die asynchrone Funktion [authenticateUser];
  • Zeile 39: Die Anfrage muss gestellt werden [POST /main.php?action=authenticate-user];
  • Zeilen 40–54: die HTTP-Anfrageoptionen;
  • Zeile 41: Dies ist eine POST-Anfrage;
  • Zeilen 42–44: Die POST-Parameter werden in einem Dokument, das der Client mit seiner Anfrage sendet, URL-kodiert;
  • Zeilen 46–49: Die Eigenschaft [data] muss den URL-kodierten POST-String enthalten. Dazu verwenden wir die in Zeile 3 importierte Bibliothek [qs];
  • Zeilen 55–69: Um die Anfrage auszuführen, verwenden wir denselben Code wie in der Methode [initSession];
  • Zeilen 73–89: Die Methode [asynchrone] ruft nacheinander die Methoden [initSession] und [authentifierUtilisateur] blockierend auf (Zeilen 77 und 82);
  • Zeile 82: Das Paar (admin, admin) wird als Anmeldedaten verwendet. Wir wissen, dass diese vom Server erkannt werden;

Die Ergebnisse der Ausführung lauten wie folgt:


[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
  • Zeilen 9–12: Benutzerauthentifizierung fehlgeschlagen: Der Server hat nicht gespeichert, dass eine JSON-Sitzung initiiert wurde. Dies liegt daran, dass das als Antwort auf die erste [init-session]-Anfrage gesendete Sitzungscookie nicht zurückgesendet wurde;

12.8. Skript [axios-04]

Das Skript [axios-04] führt zwei Verbesserungen gegenüber dem Skript [axios-03] ein:

  • Es verwaltet das Sitzungs-Cookie;
  • es fasst die gemeinsamen Elemente der Funktionen [initSession] und [authenticateUser] in einer [getRemoteData]-Funktion zusammen;

'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();

Kommentare

  • Zeilen 14–26: die Funktion [initSession]. Sie bereitet nun lediglich die an den Server zu sendende HTTP-Anfrage vor, führt sie jedoch nicht aus. Sie delegiert diese Aufgabe an die Methode [getRemoteDate] in den Zeilen 49–95;
  • Zeilen 28–47: Die Funktion [authentifierUtilisateur] folgt dem gleichen Ablauf;
  • Zeile 49: Die Funktion [getRemoteData] erhält die beiden Informationen, die sie zur Ausführung einer HTTP-Anfrage benötigt:
    • [axios], das Objekt, das für das Senden der Anfrage und den Empfang der Antwort zuständig ist;
    • [options], die Konfigurationsoptionen für die an den Server zu sendende Anfrage;
  • Zeile 59: Ausführen der Anfrage und Warten auf die JSON-Antwort;
  • Zeilen 60–68: Behandlung etwaiger Ausnahmen;
  • Zeile 64: Abrufen der Antwort, die möglicherweise im Fehlerobjekt gekapselt ist;
  • Zeile 67: Wenn der Server eine Ausnahme ausgelöst hat, ohne die Serverantwort zu enthalten, wird der empfangene Fehler an den aufrufenden Code weitergeleitet;
  • Die Funktion [getRemoteData] verwaltet das Sitzungscookie:
    • Sie speichert es in der Variablen [sessionCookie] (Zeile 11), wenn sie es zum ersten Mal empfängt;
    • sie gibt es dann bei jeder neuen HTTP-Anfrage zurück;
  • Zeilen 72–92: [getRemoteData] analysiert jede Serverantwort, um festzustellen, ob der HTTP-Header [Set-Cookie] gesendet wurde. Wir wissen, dass der Server ein Sitzungscookie namens [PHPSESSID] sendet (Zeile 10). Dies ist daher das Cookie, nach dem wir suchen (Zeile 10);
  • Zeile 72: Wir rufen die [Set-Cookie]-HTTP-Header ab, sofern vorhanden (Groß-/Kleinschreibung spielt keine Rolle). Da es tatsächlich mehrere [Set-Cookie]-Header geben kann, rufen wir ein Array ab;
  • Zeile 73: wenn ein Array von Cookies abgerufen wurde;
  • Zeilen 78–90: Wir suchen unter allen Cookies im Array nach dem Session-Cookie;
  • Zeile 80: Der relationale Ausdruck, der verwendet wird, um nach dem Sitzungs-Cookie in Cookie #i zu suchen;
  • Zeile 81: wenn der Vergleich Ergebnisse zurückgegeben hat;
  • Zeile 84: Wir haben in results[1] den ersten Teil des relationalen Ausdrucksmusters, d. h. (PHPSESSID=xxxx) bis zur schließenden Klammer (nicht enthalten), die das Sitzungs-Cookie beendet;
  • Zeilen 50–54: Bei jeder Anfrage wird das Session-Cookie in die HTTP-Header der Anfrage aufgenommen. Beim ersten Mal ist dieses Cookie leer und wird daher vom Server ignoriert;

Die Ausführungsergebnisse lauten wie folgt:


[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