12. Funciones HTTP JavaScript

12.1. Elegir una biblioteca HTTP
Aquí hemos elegido dos bibliotecas:
ECMAScript 6 tiene de forma nativa una función HTTP llamada [fetch] que no está implementada por [node.js] (a fecha de septiembre de 2019). Existe una librería llamada [node-fetch] que permite utilizar la función [fetch] en Node. Esta librería utiliza ciertas APIs específicas de [node.js]. [node-fetch] por tanto, el código puede no ser 100% portable a un entorno que no sea[node] como un navegador;
También existe una librería llamada [axios] dedicada a peticiones HTTP que es compatible tanto con [node.js] como con navegadores. Esta es la librería que utilizaremos en última instancia.
Presentaremos el mismo script escrito utilizando ambas librerías para demostrar que el enfoque de codificación es similar.
12.2. Configuración de un entorno de desarrollo
12.2.1. Instalación del servidor de cálculo de impuestos
En definitiva, escribiremos una aplicación web con la siguiente arquitectura:

JS: JavaScript
El código JavaScript es del lado del cliente:
- de un servicio que proporciona páginas estáticas o fragmentos;
- un servicio JSON;
El código JavaScript es, por tanto, un cliente JSON y, como tal, puede organizarse en capas [UI, lógica de negocio, DAO] (UI: User Interface) al igual que nuestros clientes JSON escritos en PHP.
El servidor será el servidor de cálculo de impuestos para el que ya hemos escrito 13 versiones. Vamos a escribir una decimocuarta. Comenzamos por tanto duplicando, en NetBeans, la carpeta de la versión 13 en la carpeta de la versión 14:

- en [6], modificamos el [config.json] archivo de la versión 14 como sigue:
1. {
2. "databaseFilename": "Config/database.json",
3. "rootDirectory": "C:/myprograms/laragon-lite/www/php7/scripts-web/impots/version-14",
4. "relativeDependencies": [
5.
6. "/Entities/BaseEntity.php",
7. "/Entities/Simulation.php",
8. ...
9. "views": {
10. "authentication-view.php": [700, 221, 400],
11. "tax-calculation-view.php": [200, 300, 341, 350, 800],
12. "simulation-list-view.php": [500, 600]
13. },
14. "error-views": "error-views.php"
15. }
- En la línea 3, cambiamos el directorio raíz de la aplicación'
Para acceder a este servidor, debes iniciar los [Laragon] servicios.
Una vez hecho esto, podemos probar esta nueva versión del servidor-que actualmente es idéntica a la versión 13-usando [Postman] (ver artículo enlazado). Podemos utilizar la colección de peticiones utilizadas para probar la versión 12 del servidor de cálculo de impuestos:

- en [1-4], utiliza la [init-session-700] petición para inicializar una sesión JSON;
- en [4-5], sustituye [versión-12] por [versión-14] para probar la versión 14 del proyecto;
- tras la ejecución, deberíamos recibir la respuesta JSON [6] del servidor;
La versión 14 del servidor ya está operativa. Necesitaremos modificarlo ligeramente. Vamos a revisar la API de este servidor:
Acción | Role | Contexto de ejecución |
iniciar sesión | Se utiliza para establecer el tipo (json, xml, html) de las respuestas deseadas | GET request main.php?action=init-session&type=x se puede enviar en cualquier momento |
autenticar-usuario | Autoriza o deniega el inicio de sesión de un usuario | POSTsolicitud main.php?action=authenticate-usuario La solicitud debe tener dos parámetros publicados [usuario, contraseña] Sólo se puede emitir si se conoce el tipo de sesión (json, xml, html) |
calcular-impuestos | Realiza una simulación de cálculo de impuestos | POST solicitud a main.php?action=calculate-tax La solicitud debe tener tres parámetros publicados [casado, hijos, salario] Sólo puede emitirse si se conoce el tipo de sesión (json, xml, html) y el usuario está autenticado |
lista-simulaciones | Solicitud de visualización de la lista de simulaciones realizadas desde el inicio de la sesión | GET request main.php?action=list-simulations La solicitud no acepta ningún otro parámetro Sólo puede emitirse si se conoce el tipo de sesión (json, xml, html) y el usuario está autenticado |
borrar-simulación | Elimina una simulación de la lista de simulaciones | GET request main.php?action=list-simulations&number=x La solicitud no acepta ningún otro parámetro Sólo puede emitirse si se conoce el tipo de sesión (json, xml, html) y el usuario está autenticado |
finalizar sesión | Termina la sesión de simulación. | Técnicamente, se elimina la sesión web antigua y se crea una nueva Sólo puede emitirse si se conoce el tipo de sesión (json, xml, html) y el usuario está autenticado |
12.2.2. Instalación de las bibliotecas HTTP del cliente JavaScript
Inicialmente, trabajaremos con la siguiente arquitectura:

- En [1], un script de consola [node.js] realiza una petición HTTP al servidor JSON de cálculo de impuestos;
- En [4], recibe esta respuesta y la muestra en la consola;
En el Ejemplo 1, utilizaremos las [node-fetch] y [axios] bibliotecas, y luego sólo conservaremos [axios] para los siguientes ejemplos. Ahora instalaremos estas dos librerías JavaScript desde el [VSCode] terminal:

También utilizaremos la [qs] biblioteca, que permite la codificación URL de una cadena. Recordemos que esta codificación se utiliza para codificar los parámetros de una petición HTTP GET o POST.

12.3. script [fetch-01]
El [fetch-01] script utiliza la [node-fetch] biblioteca para inicializar una sesión JSON con el servidor de cálculo de impuestos. Su código es el siguiente:
1. 'use strict';
2.
3. // imports
4. import fetch from 'node-fetch';
5. import qs from 'qs';
6. import { sprintf } from 'sprintf-js';
7. import moment from 'moment';
8.
9.
10. // Base URL of the tax calculation server
11. const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
12. // Initialize session
13. async function initSession() {
14. // HTTP request options [get /main.php?action=init-session&type=json]
15. const options = {
16. method: "GET",
17. timeout: 2000
18. };
19. // Execute the HTTP request [get /main.php?action=init-session&type=json]
20. let startFetch;
21. try {
22. // asynchronous request - [fetch] returns a promise
23. startFetch = moment(Date.now());
24. const response = await fetch(baseUrl + qs.stringify({
25. action: 'init-session',
26. type: 'json'
27. }), options);
28. // [response] is the entire HTTP response from the server (HTTP headers + the response itself)
29. // we display this response to see its structure
30. console.log(sprintf("Fetch response formatted as JSON,=%j, %s", response, time(startFetch)));
31. console.log("Fetch response in JavaScript =", response);
32. // we can access the HTTP headers
33. console.log("response headers=", response.headers);
34. // If the response type is application/json, the JSON response from the server is retrieved using the asynchronous function [response.json()]
35. // In this case, the calling code receives a [Promise] object
36. // [await] allows you to retrieve the server's [json] response rather than its promise
37. const startJson = moment(Date.now());
38. const object = await response.json();
39. console.log(sprintf("json response=%j, type=%s, %s", object, typeof(object), time(startJson)));
40. return object;
41. // if the response is of type text / plain, the server's text response is obtained with [response.text()]
42. // in this case, the calling code receives a [Promise] object
43. // [await] allows you to get the server's [text] response rather than its promise
44. // const text = await response.text();
45. // console.log("text response=", text);
46. // return text;
47. } catch (error) {
48. // We're here because the server sent an error code [404 Not Found, ...] with an empty body—we log the error to see its structure
49. // or because the client [fetch] threw an exception (network unreachable, ...)
50. // we display the error structure
51. console.log(sprintf("error fetch in json=%j, %s", error, time(startFetch)));
52. console.log("fetch error in JavaScript=", typeof(error), error);
53. // we log the received error message
54. throw error.message;
55. }
56. }
57.
58. // The main function executes the asynchronous function [initSession]
59. async function main() {
60. try {
61. console.log("HTTP request to the server in progress ---------------------------------------------");
62. const response = await initSession();
63. console.log("success ---------------------------------------------");
64. console.log("response=", response, typeof (response))
65. } catch (error) {
66. console.log("error ---------------------------------------------");
67. console.log("error=", error, typeof(error));
68. }
69. }
70.
71. // test
72. main();
73.
74. // utility for displaying time and duration
75. function getTime(start) {
76. // current time
77. const now = moment(Date.now());
78. // time formatting
79. let result = "time=" + now.format("HH:mm:ss:SSS");
80. // Do we need to calculate a duration?
81. if (start) {
82. const duration = now - start;
83. const milliseconds = duration % 1000;
84. const seconds = Math.floor(duration / 1000);
85. // Format time + duration
86. result = result + sprintf(", duration= %s seconds and %s milliseconds", seconds, milliseconds);
87. }
88. // result
89. return result;
90. }
Comentarios
- Las funciones HTTP de JavaScript son funciones asíncronas. Aquí estamos aplicando lo aprendido en la sección anterior (véase enlace);
- línea 24: para esperar a que la respuesta de la función asíncrona [fetch] se publique en el [node.js] bucle de eventos, utilizamos la palabra clave [await]. Sabemos que esta declaración debe estar dentro de código prefijado por la palabra clave [async] (línea 13);
- líneas 13-56: encapsulamos el código HTTP dentro de la función asíncrona [initSession];
- líneas 59-69: una segunda función asíncrona [main] se utiliza para llamar a la función asíncrona [initSession] de forma bloqueante (async/await);
- línea 72: se llama a la función asíncrona [main] ;
- aunque todo el código parece código síncrono, en realidad se trata de funciones asíncronas que se ejecutan, pero de forma bloqueante;
- línea 19: para inicializar una sesión JSON con el servidor de cálculo de impuestos, debes enviarle la petición HTTP [get /main.php?action=init-session&type=json]. Esto es lo que hace el código de las líneas 24-27. La sintaxis para [fetch] es la siguiente: [fetch(URL, options)] con:
- [URL]: la URL que se está consultando;
- [opciones]: un objeto que define las opciones de la petición. Aquí es donde definimos las cabeceras HTTP que queremos enviar al servidor de destino;
- líneas 15-18: definimos las opciones de la petición que queremos realizar:
- [método]: queremos realizar un GET;
- [timeout]: queremos que el [fetch] cliente no espere más de 2 segundos la respuesta del servidor. Si se supera este tiempo de espera, [fetch] lanzará una excepción;
- línea 24: para obtener la URL [/main.php?action=init-session&type=json], utilizamos la [qs] biblioteca para obtener los parámetros codificados en la URL [action,type] de la petición GET. La cadena resultante es [init-session&type=json], que podríamos haber construido nosotros mismos. Simplemente queríamos demostrar cómo obtener una cadena codificada en URL;
- línea 24: la palabra clave [await] indica que aquí se está lanzando una tarea asíncrona y que estamos esperando a que publique su respuesta en el [node.js] bucle de eventos;
- línea 24: en [response], obtenemos un objeto complejo que describe toda la respuesta HTTP recibida (cabeceras y cuerpo);
- líneas 30-31: Imprimimos el [respuesta] objeto para ver su estructura, primero como cadena y luego como objeto JavaScript;
- línea 33: mostramos las cabeceras HTTP enviadas por el servidor;
- Línea 38: Sabemos que el servidor de cálculo de impuestos enviará una cadena JSON. Esta cadena está encapsulada en el objeto [response] . Podemos recuperarla mediante el método [response.json()] . Sin embargo, este método es asíncrono. Por tanto, escribimos [await response.json()] para recuperar la cadena JSON que se publicará en el [node.js] bucle de eventos. De hecho, no es la cadena JSON en sí lo que obtenemos, sino el objeto JavaScript representado por ella;
- línea 39: visualización de la cadena JSON recibida;
- línea 40: devuelve el objeto JavaScript recibido;
- línea 47: capturamos cualquier error potencial de la [fetch] declaración. Esto sólo lanza una excepción si la operación HTTP falló y no se recibió ninguna respuesta del servidor. Si se recibió una respuesta, incluso con un código de estado HTTP distinto de [200 OK], [fetch] no lanza una excepción, y la respuesta del servidor estará disponible en la línea 38;
- líneas 51-52: se muestra el [error] objeto recibido por la [catch] cláusula, primero como cadena JSON y luego como objeto JavaScript;
- línea 54: el mensaje de error de [fetch] se almacena en [error.message];
- líneas 59-69: la función asíncrona [main] llama a la función asíncrona [initSession] de forma bloqueante (await en la línea 62);
- línea 72: la función asíncrona [main] se lanza, y el código del script principal se completa. El script global terminará cuando las tareas asíncronas lanzadas hayan publicado sus resultados en el bucle de eventos;
Los resultados de la ejecución son los siguientes:
Caso 1: el servidor Laragon no se está ejecutando
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-01.js"
2. HTTP request to the server in progress ---------------------------------------------
3. Fetch error in JSON: {"message":"network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json","type":"request-timeout"}, time=10:08:48:180, duration= 2 seconds and 62 milliseconds
4. fetch error in JavaScript = object { FetchError: network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json
5. at Timeout.<anonymous> (c:\Data\st-2019\dev\es6\javascript\node_modules\node-fetch\lib\index.js:1448:13)
6. at ontimeout (timers.js:436:11)
7. at tryOnTimeout (timers.js:300:5)
8. at listOnTimeout (timers.js:263:5)
9. at Timer.processTimers (timers.js:223:10)
10. message:
11. 'network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
12. type: 'request-timeout' }
13. error ---------------------------------------------
14. error= network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json string
15.
16. [Done] exited with code=0 in 2.804 seconds
Comentarios
- línea 3: la petición HTTP falla tras 2 segundos y 62 milisegundos debido al tiempo de espera de 2 segundos impuesto a la petición HTTP;
- líneas 4-9: el [error] objeto JavaScript interceptado por la [catch(error)] cláusula. Este objeto tiene dos propiedades:
- [FetchError]: línea 4;
- [mensaje]: líneas 10-12;
- línea 14: el mensaje de error recibido por la función asíncrona [main];
Caso 2: El servidor Laragon se está ejecutando
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-01.js"
2. HTTP request to the server in progress ---------------------------------------------
3. Fetch response formatted in JSON,={"size":0,"timeout":2000}, time=10:13:50:814, duration= 0 seconds and 375 milliseconds
4. Fetch response in JavaScript = Response {
5. size: 0,
6. timeout: 2000,
7. [Symbol(Body internals)]:
8. { body:
9. PassThrough {
10. _readableState: [ReadableState],
11. readable: true,
12. domain: null,
13. _events: [Object],
14. _eventsCount: 2,
15. _maxListeners: undefined,
16. _writableState: [WritableState],
17. writable: false,
18. allowHalfOpen: true,
19. _transformState: [Object] },
20. disturbed: false,
21. error: null },
22. [Symbol(Response internals)]:
23. { url:
24. 'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
25. status: 200,
26. statusText: 'OK',
27. headers: Headers { [Symbol(map)]: [Object] },
28. counter: 0 } }
29. response headers = Headers {
30. [Symbol(map)]:
31. [Object: null prototype] {
32. date: [ 'Sat, 14 Sep 2019 08:13:50 GMT' ],
33. server: [ 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11' ],
34. 'x-powered-by': [ 'PHP/7.2.11' ],
35. 'cache-control': [ 'max-age=0, private, must-revalidate, no-cache, private' ],
36. 'set-cookie': [ 'PHPSESSID=99q2iinusmhl55fa600aie2mmu; path=/' ],
37. 'content-length': [ '86' ],
38. connection: [ 'close' ],
39. 'content-type': [ 'application/json' ] } }
40. response json={"action":"init-session","status":700,"response":"session started with type [json]"}, type=object, time=10:13:50:825, duration= 0 seconds and 1 milliseconds
41. success ---------------------------------------------
42. response = { action: 'init-session',
43. 'status': 700,
44. 'response': 'session started with type [json]' } object
45.
46. [Done] exited with code=0 in 1.022 seconds
Comentarios
- línea 3: [fetch] recibe la respuesta del servidor tras 375 ms;
- líneas 4-39: la estructura del objeto JavaScript [response] encapsulando la respuesta del servidor. Entre sus propiedades, algunas pueden interesarnos:
- [status] (línea 25): Código de estado HTTP de la respuesta del servidor'
- [statusText] (línea 26): texto asociado a este código;
- [cabeceras] (línea 27): las cabeceras HTTP de la respuesta del servidor;
- [body] (línea 8): representa el documento enviado por el servidor. La sentencia [fetch] proporciona métodos para trabajar con él;
- líneas 29-39: las cabeceras HTTP de la respuesta del servidor;
- línea 40: la función asíncrona [response.json()] devuelve su respuesta tras 1 milisegundo;
- líneas 42-44: el objeto JavaScript recibido por la función asíncrona [main];
Caso 3: El servidor Laragon se está ejecutando, pero se le envía un comando incorrecto:

- arriba, línea 26, se pasa un tipo de sesión incorrecto al servidor;
Los resultados de la ejecución son los siguientes:
1. HTTP request to the server in progress ---------------------------------------------
2. Fetch response formatted as JSON,={"size":0,"timeout":2000}, time=10:27:54:114, duration= 0 seconds and 136 milliseconds
3. Fetch response in JavaScript = Response {
4. size: 0,
5. timeout: 2000,
6. [Symbol(Body internals)]:
7. { body:
8. PassThrough {
9. _readableState: [ReadableState],
10. readable: true,
11. domain: null,
12. _events: [Object],
13. _eventsCount: 2,
14. _maxListeners: undefined,
15. _writableState: [WritableState],
16. writable: false,
17. allowHalfOpen: true,
18. _transformState: [Object] },
19. disturbed: false,
20. error: null },
21. [Symbol(Response internals)]:
22. { url:
23. 'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=x',
24. status: 400,
25. statusText: 'Bad Request',
26. headers: Headers { [Symbol(map)]: [Object] },
27. counter: 0 } }
28. response headers = Headers {
29. [Symbol(map)]:
30. [Object: null prototype] {
31. date: [ 'Sat, 14 Sep 2019 08:27:54 GMT' ],
32. server: [ 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11' ],
33. 'x-powered-by': [ 'PHP/7.2.11' ],
34. 'cache-control': [ 'max-age=0, private, must-revalidate, no-cache, private' ],
35. 'set-cookie': [ 'PHPSESSID=5ku9gfok81ikj98hia0meeum57; path=/' ],
36. 'content-length': [ '79' ],
37. connection: [ 'close' ],
38. 'content-type': [ 'application/json' ] } }
39. response json={"action":"init-session","status":703,"response":"invalid type=[x] parameter"}, type=object, time=10:27:54:127, duration= 0 seconds and 2 milliseconds
40. success ---------------------------------------------
41. response = { action: 'init-session',
42. 'status': 703,
43. 'response': 'invalid type=[x] parameter' } object
44.
45. [Done] exited with code=0 in 0.712 seconds
- La respuesta del servidor se recibe en la línea 2;
- línea 24: podemos ver que el código de estado HTTP de la respuesta del servidor es 400, un código de error. Sin embargo, [fetch] no lanza una excepción. Mientras [fetch] reciba una respuesta del servidor, la procesa y no lanza una excepción;
- líneas 41-43: la respuesta obtenida por la función asíncrona [main];
12.4. script [fetch-02]
El siguiente script reutiliza el [fetch-01] script a la vez que elimina todos los detalles innecesarios:
1. 'use strict';
2.
3. // imports
4. import fetch from 'node-fetch';
5. import qs from 'qs';
6.
7. // Base URL of the tax calculation server
8. const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
9. // Initialize session
10. async function initSession() {
11. // HTTP request options [get /main.php?action=init-session&type=json]
12. const options = {
13. method: "GET",
14. timeout: 2000
15. };
16. // Execute the HTTP request [get /main.php?action=init-session&type=json]
17. const response = await fetch(baseUrl + qs.stringify({
18. action: 'init-session',
19. type: 'json'
20. }), options);
21. // Result received in JSON
22. return await response.json();
23. }
24.
25. // The main function executes the asynchronous function [initSession]
26. async function main() {
27. try {
28. console.log("HTTP request to the server in progress ---------------------------------------------");
29. const response = await initSession();
30. console.log("success ---------------------------------------------");
31. console.log("response=", response)
32. } catch (error) {
33. console.log("error ---------------------------------------------");
34. console.log("error=", error.message);
35. }
36. }
37.
38. // test
39. main();
Resultados de una ejecución normal:
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-02.js"
2. HTTP request to the server in progress ---------------------------------------------
3. success ---------------------------------------------
4. response= { action: 'init-session',
5. 'status': 700,
6. 'response': 'session started with type [json]' }
7.
8. [Done] exited with code=0 in 0.56 seconds
Resultado de una ejecución con una excepción (parada del servidor Laragon):
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-02.js"
2. HTTP request to the server in progress ---------------------------------------------
3. error ---------------------------------------------
4. error= network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json
5.
6. [Done] exited with code=0 in 2.701 seconds
12.5. script [axios-01]
Aquí revisitamos el [fetch-01] script, que reescribimos usando la [axios] librería. Como recordatorio, nuestro interés en esta biblioteca radica en su portabilidad entre el [node.js] entorno y los navegadores comunes. Esto permite:
- En la Fase 1, probar nuestros scripts en un [Node.js] entorno;
- en la fase 2, portarlos a un navegador;
El [axios-01] script sigue la estructura del [fetch-01] script:
1. 'use strict';
2. import axios from 'axios';
3.
4. // default axios configuration
5. axios.defaults.timeout = 2000;
6. axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
7.
8. // initialize session
9. async function initSession(axios) {
10. // HTTP request options [get /main.php?action=init-session&type=json]
11. const options = {
12. method: "GET",
13. // URL parameters
14. params: {
15. action: 'init-session',
16. type: 'json'
17. }
18. };
19. // Execute the HTTP request [get /main.php?action=init-session&type=json]
20. try {
21. // asynchronous request
22. const response = await axios.request('main.php', options);
23. // response is the entire HTTP response from the server (HTTP headers + the response body)
24. // display this response to see its structure
25. console.log("axios response=", response);
26. // The server's response is in [response.data]
27. return response.data;
28. } catch (error) {
29. // we're here because the server sent an error code [404 Not Found, 500 Internal Server Error, ...]
30. // the [error] parameter is an exception instance—it can take various forms
31. // we log it to see its structure
32. console.log("axios error=", typeof (error), error);
33. if (error.response) {
34. // The server reported an error in the HTTP status, but it also sent a response
35. // so this is found in [error.response.data]
36. // we know that the server sends JSON responses with the structure {action, status, response}
37. // and that in case of an error, the error message is in [response]
38. return error.response.data;
39. } else {
40. // we throw the error
41. throw error;
42. }
43. }
44. }
45.
46. // The main function executes the asynchronous function [initSession]
47. async function main() {
48. try {
49. console.log("HTTP request to the server in progress ---------------------------------------------");
50. const response = await initSession(axios);
51. console.log("success ---------------------------------------------");
52. console.log("response=", response, typeof (response))
53. } catch (error) {
54. console.log("error ---------------------------------------------");
55. console.log("error=", error.message);
56. }
57. }
58.
59. // test
60. main();
Comentarios
- línea 2: importamos la [axios] biblioteca;
- líneas 5-6: configuración por defecto para peticiones HTTP. Las [axios.defaults] opciones se aplican a todas las peticiones HTTP enviadas por el [axios] objeto sin necesidad de especificarlas para cada nueva petición;
- línea 5: tiempo de espera de 2 segundos para todas las peticiones;
- línea 6: todas las URL serán relativas a la URL base;
- línea 9: la asíncrona [initSession] función;
- líneas 11-18: las opciones de la petición HTTP a enviar (además de las opciones por defecto ya definidas en las líneas 5-6);
- líneas 14-17: los parámetros URL [action=init-session&type=json]. El [params] objeto se convertirá automáticamente en una cadena codificada en URL;
- línea 22: llamada de bloqueo a la función asíncrona [axios.request]. El primer parámetro es la URL de destino construida como [main.php] aplicada a la URL base definida en la línea 6. El segundo parámetro es el [opciones] objeto de las líneas 11-18;
- línea 25: [respuesta] es un objeto JavaScript que encapsula toda la respuesta HTTP del servidor (cabeceras HTTP + cuerpo de la respuesta). Lo desplegamos para ver su estructura JavaScript;
- línea 27: si el servidor envió un documento, entonces se encuentra en [response.data]. Aquí sabemos que el servidor envía una respuesta JSON acompañada de la cabecera HTTP [Content-type: application/json]. La presencia de esta cabecera hace que [axios] deserialice automáticamente [response.data] en un objeto JavaScript;
- línea 28: la función [axios] puede lanzar una excepción. Aquí es donde [axios] difiere de [fetch]. Se lanza una excepción en los siguientes casos:
- no se ha podido enviar la solicitud HTTP (error del lado del cliente);
- el servidor envió un código de error HTTP (400, 404, 500, etc.) (este comportamiento es realmente configurable). Nótese que si este código de estado HTTP va acompañado de una respuesta, [fetch] no lanza una excepción, mientras que [axios] sí lo hace. Sin embargo, si el código de error HTTP va acompañado de un documento, se coloca en [error.response];
- línea 32: mostramos la estructura JavaScript del [error] objeto;
- líneas 33-38: si el [error] objeto contiene un [respuesta] objeto, esta respuesta se devuelve al código de llamada;
- líneas 39-42: en todos los demás casos, el [error] objeto se devuelve al código de llamada;
- líneas 47-57: la función asíncrona [main];
- línea 50: llamada de bloqueo a la función asíncrona [initSession]. La respuesta JSON del servidor se recupera como un objeto JavaScript;
- líneas 53-56: manejo de cualquier error. El mensaje de error está en [error.message];
Los resultados de la ejecución son los siguientes:
Caso 1: El servidor Laragon no se está ejecutando
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
2. HTTP request to the server in progress ---------------------------------------------
3. axios error= object { Error: timeout of 2000ms exceeded
4. at createError (c:\Data\st-2019\dev\es6\javascript\node_modules\axios\lib\core\createError.js:16:15)
5. at Timeout.handleRequestTimeout (c:\Data\st-2019\dev\es6\javascript\node_modules\axios\lib\adapters\http.js:252:16)
6. at ontimeout (timers.js:436:11)
7. at tryOnTimeout (timers.js:300:5)
8. at listOnTimeout (timers.js:263:5)
9. at Timer.processTimers (timers.js:223:10)
10. config:
11. { url:
12. 'http://localhost/php7/scripts-web/impots/version-14/main.php',
13. method: 'get',
14. params: { action: 'init-session', type: 'json' },
15. headers:
16. { Accept: 'application/json, text/plain, */*',
17. 'User-Agent': 'axios/0.19.0' },
18. baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
19. transformRequest: [ [Function: transformRequest] ],
20. transformResponse: [ [Function: transformResponse] ],
21. timeout: 2000,
22. adapter: [Function: httpAdapter],
23. xsrfCookieName: 'XSRF-TOKEN',
24. xsrfHeaderName: 'X-XSRF-TOKEN',
25. maxContentLength: -1,
26. validateStatus: [Function: validateStatus],
27. data: undefined },
28. code: 'ECONNABORTED',
29. request:
30. Writable {
31. _writableState:
32. WritableState {
33. objectMode: false,
34. highWaterMark: 16384,
35. finalCalled: false,
36. needDrain: false,
37. ending: false,
38. ended: false,
39. finished: false,
40. destroyed: false,
41. decodeStrings: true,
42. defaultEncoding: 'utf8',
43. length: 0,
44. writing: false,
45. corked: 0,
46. sync: true,
47. bufferProcessing: false,
48. onwrite: [Function: bound onwrite],
49. writecb: null,
50. writelen: 0,
51. bufferedRequest: null,
52. lastBufferedRequest: null,
53. pendingcb: 0,
54. prefinished: false,
55. errorEmitted: false,
56. emitClose: true,
57. bufferedRequestCount: 0,
58. corkedRequestsFree: [Object] },
59. writable: true,
60. domain: null,
61. _events:
62. [Object: null prototype] {
63. response: [Function: handleResponse],
64. error: [Function: handleRequestError] },
65. _eventsCount: 2,
66. _maxListeners: undefined,
67. _options:
68. { protocol: 'http:',
69. maxRedirects: 21,
70. maxBodyLength: 10485760,
71. path:
72. '/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
73. method: 'GET',
74. headers: [Object],
75. agent: undefined,
76. auth: undefined,
77. hostname: 'localhost',
78. port: null,
79. nativeProtocols: [Object],
80. pathname: '/php7/scripts-web/impots/version-14/main.php',
81. search: '?action=init-session&type=json' },
82. _redirectCount: 0,
83. _redirects: [],
84. _requestBodyLength: 0,
85. _requestBodyBuffers: [],
86. _onNativeResponse: [Function],
87. _currentRequest:
88. ClientRequest {
89. domain: null,
90. _events: [Object],
91. _eventsCount: 6,
92. _maxListeners: undefined,
93. output: [],
94. outputEncodings: [],
95. outputCallbacks: [],
96. outputSize: 0,
97. writable: true,
98. _last: true,
99. chunkedEncoding: false,
100. shouldKeepAlive: false,
101. useChunkedEncodingByDefault: false,
102. sendDate: false,
103. _removedConnection: false,
104. _removedContLen: false,
105. _removedTE: false,
106. _contentLength: 0,
107. _hasBody: true,
108. _trailer: '',
109. finished: true,
110. _headerSent: true,
111. socket: [Socket],
112. connection: [Socket],
113. _header:
114. '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',
115. _onPendingData: [Function: noopPendingOutput],
116. agent: [Agent],
117. socketPath: undefined,
118. timeout: undefined,
119. method: 'GET',
120. path:
121. '/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
122. _ended: false,
123. res: null,
124. aborted: 1568528450762,
125. timeoutCb: null,
126. upgradeOrConnect: false,
127. parser: [HTTPParser],
128. maxHeadersCount: null,
129. _redirectable: [Circular],
130. [Symbol(isCorked)]: false,
131. [Symbol(outHeadersKey)]: [Object] },
132. _currentUrl:
133. 'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json' },
134. response: undefined,
135. isAxiosError: true,
136. toJSON: [Function] }
137. error ---------------------------------------------
138. error= 2000ms timeout exceeded
139.
140. [Done] exited with code=0 in 2.784 seconds
Comentarios
- líneas 33-136: el [error] objeto contiene mucha información;
- [Error], líneas 3-9: una descripción del error que se ha producido;
- [config], líneas 10-27: la configuración de la petición HTTP que ha provocado este error;
- [config.url], líneas 11-12: la URL de destino;
- [config.method], línea 13: método de solicitud;
- [config.params], línea 14: los parámetros de la URL;
- [config.headers], líneas 16-17: las cabeceras HTTP de la petición;
- [config.baseURL], línea 18: la URL base de la URL de destino;
- [config.timeout], línea 21: el tiempo de espera de la petición;
- [code], línea 28: un código de error;
- [request], líneas 29-133: una descripción detallada de la petición HTTP. Tenga en cuenta que la mayoría de las propiedades tienen como prefijo un guión bajo (_), lo que indica que son propiedades internas del [request]objeto no destinadas a ser utilizadas directamente por el desarrollador;
- [respuesta], línea 134: la respuesta del servidor, que aquí está vacía;
- línea 138: el mensaje de error que muestra la [main] función;
Caso 2: El servidor Laragon se está ejecutando
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
2. HTTP request to the server in progress ---------------------------------------------
3. Axios response: { status: 200,
4. statusText: 'OK',
5. headers:
6. { date: 'Sun, 15 Sep 2019 07:09:26 GMT',
7. server: 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11',
8. 'x-powered-by': 'PHP/7.2.11',
9. 'cache-control': 'max-age=0, private, must-revalidate, no-cache, private',
10. 'set-cookie': [ 'PHPSESSID=uas6lugtblstktcifpd8e5irm6; path=/' ],
11. 'content-length': '86',
12. connection: 'close',
13. 'content-type': 'application/json' },
14. config:
15. { url:
16. 'http://localhost/php7/scripts-web/impots/version-14/main.php',
17. method: 'get',
18. params: { action: 'init-session', type: 'json' },
19. headers:
20. { Accept: 'application/json, text/plain, */*',
21. 'User-Agent': 'axios/0.19.0' },
22. baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
23. transformRequest: [ [Function: transformRequest] ],
24. transformResponse: [ [Function: transformResponse] ],
25. timeout: 2000,
26. adapter: [Function: httpAdapter],
27. xsrfCookieName: 'XSRF-TOKEN',
28. xsrfHeaderName: 'X-XSRF-TOKEN',
29. maxContentLength: -1,
30. validateStatus: [Function: validateStatus],
31. data: undefined },
32. request:
33. ClientRequest {
34. domain: null,
35. _events:
36. [Object: null prototype] {
37. socket: [Function],
38. abort: [Function],
39. aborted: [Function],
40. error: [Function],
41. timeout: [Function],
42. prefinish: [Function: requestOnPrefinish] },
43. _eventsCount: 6,
44. _maxListeners: undefined,
45. output: [],
46. outputEncodings: [],
47. outputCallbacks: [],
48. outputSize: 0,
49. writable: true,
50. _last: true,
51. chunkedEncoding: false,
52. shouldKeepAlive: false,
53. useChunkedEncodingByDefault: false,
54. sendDate: false,
55. _removedConnection: false,
56. _removedContLen: false,
57. _removedTE: false,
58. _contentLength: 0,
59. _hasBody: true,
60. _trailer: '',
61. finished: true,
62. _headerSent: true,
63. socket:
64. Socket {
65. connecting: false,
66. _hadError: false,
67. _handle: [TCP],
68. _parent: null,
69. _host: 'localhost',
70. _readableState: [ReadableState],
71. readable: true,
72. domain: null,
73. _events: [Object],
74. _eventsCount: 7,
75. _maxListeners: undefined,
76. _writableState: [WritableState],
77. writable: false,
78. allowHalfOpen: false,
79. _sockname: null,
80. _pendingData: null,
81. _pendingEncoding: '',
82. server: null,
83. _server: null,
84. parser: null,
85. _httpMessage: [Circular],
86. [Symbol(asyncId)]: 6,
87. [Symbol(lastWriteQueueSize)]: 0,
88. [Symbol(timeout)]: null,
89. [Symbol(kBytesRead)]: 0,
90. [Symbol(kBytesWritten)]: 0 },
91. connection:
92. Socket {
93. connecting: false,
94. _hadError: false,
95. _handle: [TCP],
96. _parent: null,
97. _host: 'localhost',
98. _readableState: [ReadableState],
99. readable: true,
100. domain: null,
101. _events: [Object],
102. _eventsCount: 7,
103. _maxListeners: undefined,
104. _writableState: [WritableState],
105. writable: false,
106. allowHalfOpen: false,
107. _sockname: null,
108. _pendingData: null,
109. _pendingEncoding: '',
110. server: null,
111. _server: null,
112. parser: null,
113. _httpMessage: [Circular],
114. [Symbol(asyncId)]: 6,
115. [Symbol(lastWriteQueueSize)]: 0,
116. [Symbol(timeout)]: null,
117. [Symbol(kBytesRead)]: 0,
118. [Symbol(kBytesWritten)]: 0 },
119. _header:
120. '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',
121. _onPendingData: [Function: noopPendingOutput],
122. agent:
123. Agent {
124. domain: null,
125. _events: [Object],
126. _eventsCount: 1,
127. _maxListeners: undefined,
128. defaultPort: 80,
129. protocol: 'http:',
130. options: [Object],
131. requests: {},
132. sockets: [Object],
133. freeSockets: {},
134. keepAliveMsecs: 1000,
135. keepAlive: false,
136. maxSockets: Infinity,
137. maxFreeSockets: 256 },
138. socketPath: undefined,
139. timeout: undefined,
140. method: 'GET',
141. path:
142. '/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
143. _ended: true,
144. res:
145. IncomingMessage {
146. _readableState: [ReadableState],
147. readable: false,
148. domain: null,
149. _events: [Object],
150. _eventsCount: 3,
151. _maxListeners: undefined,
152. socket: [Socket],
153. connection: [Socket],
154. httpVersionMajor: 1,
155. httpVersionMinor: 0,
156. httpVersion: '1.0',
157. complete: true,
158. headers: [Object],
159. rawHeaders: [Array],
160. trailers: {},
161. rawTrailers: [],
162. aborted: false,
163. upgrade: false,
164. url: '',
165. method: null,
166. statusCode: 200,
167. statusMessage: 'OK',
168. client: [Socket],
169. _consuming: false,
170. _dumped: false,
171. req: [Circular],
172. responseUrl:
173. 'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
174. redirects: [] },
175. aborted: undefined,
176. timeoutCb: null,
177. upgradeOrConnect: false,
178. parser: null,
179. maxHeadersCount: null,
180. _redirectable:
181. Writable {
182. _writableState: [WritableState],
183. writable: true,
184. domain: null,
185. _events: [Object],
186. _eventsCount: 2,
187. _maxListeners: undefined,
188. _options: [Object],
189. _redirectCount: 0,
190. _redirects: [],
191. _requestBodyLength: 0,
192. _requestBodyBuffers: [],
193. _onNativeResponse: [Function],
194. _currentRequest: [Circular],
195. _currentUrl:
196. 'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json' },
197. [Symbol(isCorked)]: false,
198. [Symbol(outHeadersKey)]:
199. [Object: null prototype] { accept: [Array], 'user-agent': [Array], host: [Array] } },
200. data:
201. { action: 'init-session',
202. 'status': 700,
203. 'response': 'session started with type [json]' } }
204. success ---------------------------------------------
205. response = { action: 'init-session',
206. 'status': 700,
207. 'response': 'session started with type [json]' } object
208.
209. [Done] exited with code=0 in 1.115 seconds
Comentarios
- líneas 3-203: el objeto JavaScript [response] que encapsula la respuesta HTTP del servidor;
- línea 3, [status]: el código de estado HTTP de la respuesta;
- línea 4, [statusText]: el texto asociado al código de estado HTTP anterior;
- líneas 5-13, [cabeceras]: las cabeceras HTTP de la respuesta:
- línea 10, [Set-Cookie]: la cookie de sesión;
- línea 13, [Content-Type]: el tipo de documento enviado por el servidor;
- líneas 14-31, [config]: la configuración de la petición HTTP enviada;
- líneas 32-199, [request]: el objeto JavaScript que detalla la petición HTTP enviada;
- líneas 200-203, [request.data]: el objeto JavaScript que encapsula la respuesta JSON del servidor;
- líneas 205-207: la respuesta recuperada por la función [main] asíncrona;
Caso 3: Se envía una [init-session] petición incorrecta al servidor Laragon;

Los resultados de la ejecución son los siguientes:
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
2. HTTP request to the server in progress ---------------------------------------------
3. axios error= object { Error: Request failed with status code 400
4. ...
5. config:
6. { url:
7. 'http://localhost/php7/scripts-web/impots/version-14/main.php',
8. ...
9. data: undefined },
10. request:
11. ...
12. [Symbol(outHeadersKey)]:
13. [Object: null prototype] { accept: [Array], 'user-agent': [Array], host: [Array] } },
14. response:
15. { status: 400,
16. statusText: 'Bad Request',
17. headers:
18. { date: 'Sun, 15 Sep 2019 07:25:58 GMT',
19. server: 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11',
20. 'x-powered-by': 'PHP/7.2.11',
21. 'cache-control': 'max-age=0, private, must-revalidate, no-cache, private',
22. 'set-cookie': [Array],
23. 'content-length': '79',
24. connection: 'close',
25. 'content-type': 'application/json' },
26. config:
27. { url:
28. 'http://localhost/php7/scripts-web/impots/version-14/main.php',
29. ...
30. data: undefined },
31. request:
32. ...
33. [Symbol(outHeadersKey)]: [Object] },
34. data:
35. { action: 'init-session',
36. 'status': 703,
37. 'response': 'invalid type=[x] parameter' } },
38. isAxiosError: true,
39. toJSON: [Function] }
40. success ---------------------------------------------
41. response = { action: 'init-session',
42. 'status': 703,
43. 'response': 'invalid type=[x] parameter' } object
44.
45. [Done] exited with code=0 in 0.69 seconds
Dado que el servidor respondió con un código HTTP 400 (línea 15), [axios] lanzó una excepción (de nuevo, este comportamiento es configurable). Aunque [axios] lanzó una excepción, seguimos obteniendo la respuesta HTTP del servidor en [error.response] (línea 14) y el documento JSON enviado en [error.response.data] (línea 34). Líneas 41-43: la función [main] recupera correctamente la respuesta JSON del servidor.
12.6. script [axios-02]
Ahora que hemos detallado los objetos que maneja la [axios] biblioteca durante una petición HTTP, podemos reescribir el [axios-01] script de la siguiente manera:
1. 'use strict';
2. import axios from 'axios';
3.
4. // default axios configuration
5. axios.defaults.timeout = 2000;
6. axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
7.
8. // initialize session
9. async function initSession(axios) {
10. // HTTP request options [get /main.php?action=init-session&type=json]
11. const options = {
12. method: "GET",
13. // URL parameters
14. params: {
15. action: 'init-session',
16. type: 'json'
17. }
18. };
19. try {
20. // Execute the HTTP request [get /main.php?action=init-session&type=json]
21. const response = await axios.request('main.php', options);
22. // the server response is in [response.data]
23. return response.data;
24. } catch (error) {
25. // server response
26. if (error.response) {
27. // The JSON response is in [error.response.data]
28. return error.response.data;
29. } else {
30. // throw the error
31. throw error;
32. }
33. }
34. }
35.
36. // The main function executes the asynchronous function [initSession]
37. async function main() {
38. try {
39. console.log("HTTP request to the server in progress ---------------------------------------------");
40. const response = await initSession(axios);
41. console.log("success ---------------------------------------------");
42. console.log("response=", response, typeof (response))
43. } catch (error) {
44. console.log("error ---------------------------------------------");
45. console.log("error=", error.message);
46. }
47. }
48.
49. // test
50. main();
12.7. script [axios-03]
El [axios-03] script sigue la misma metodología que el [axios-02] script. Esta vez, añadimos la [authenticate-user] petición HTTP al servidor, que se envía mediante una petición POST:
1. 'use strict';
2. import axios from 'axios';
3. import qs from 'qs'
4.
5. // axios configuration
6. axios.defaults.timeout = 2000;
7. axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
8.
9.
10. // initialize session
11. async function initSession(axios) {
12. // HTTP request options [get /main.php?action=init-session&type=json]
13. const options = {
14. method: "GET",
15. // URL parameters
16. params: {
17. action: 'init-session',
18. type: 'json'
19. }
20. };
21. try {
22. // Execute the HTTP request [get /main.php?action=init-session&type=json]
23. const response = await axios.request('main.php', options);
24. // the server response is in [response.data]
25. return response.data;
26. } catch (error) {
27. // server response
28. if (error.response) {
29. // The JSON response is in [error.response.data]
30. return error.response.data;
31. } else {
32. // Re-throw the error
33. throw error;
34. }
35. }
36. }
37.
38. async function authenticateUser(axios, user, password) {
39. // HTTP request options [POST /main.php?action=authenticate-user]
40. const options = {
41. method: "POST",
42. headers: {
43. 'Content-type': 'application/x-www-form-urlencoded',
44. },
45. // POST body
46. data: qs.stringify({
47. user: user,
48. password: password
49. }),
50. // URL parameters
51. params: {
52. action: 'authenticate-user'
53. }
54. };
55. try {
56. // Execute the HTTP request [post /main.php?action=authenticate-user]
57. const response = await axios.request('main.php', options);
58. // the server response is in [response.data]
59. return response.data;
60. } catch (error) {
61. // server response
62. if (error.response) {
63. // The JSON response is in [error.response.data]
64. return error.response.data;
65. } else {
66. // throw the error
67. throw error;
68. }
69. }
70. }
71.
72. // The main function executes the asynchronous functions one by one
73. async function main() {
74. try {
75. // init-session
76. console.log("init-session action in progress ---------------------------------------------");
77. const response1 = await initSession(axios);
78. console.log("success ---------------------------------------------");
79. console.log("response=", response1);
80. // authenticate-user
81. console.log("authenticate-user action in progress ---------------------------------------------");
82. const response2 = await authenticateUser(axios, 'admin', 'admin');
83. console.log("success ---------------------------------------------");
84. console.log("response=", response2)
85. } catch (error) {
86. console.log("error ---------------------------------------------");
87. console.log("error=", error);
88. }
89. }
90.
91. // test
92. main();
Comentarios
- líneas 38-70: la función asíncrona [authenticateUser];
- línea 39: la solicitud debe realizarse [POST /main.php?action=authenticate-user];
- líneas 40-54: las opciones de la petición HTTP;
- línea 41: se trata de una solicitud POST;
- líneas 42-44: los parámetros POST se codificarán en URL en un documento que el cliente envía con su solicitud;
- líneas 46-49: la [data] propiedad debe contener la cadena POST codificada en URL. Para ello, utilizamos la [qs] biblioteca importada en la línea 3;
- líneas 55-69: para ejecutar la petición, utilizamos el mismo código que en el [initSession] método;
- líneas 73-89: el [asynchrone] método llama sucesivamente a los [initSession] y[authentifierUtilisateur] métodos de forma bloqueante, líneas 77 y 82;
- línea 82: el par (admin, admin) se utiliza como credenciales de inicio de sesión. Sabemos que son reconocidas por el servidor;
Los resultados de la ejecución son los siguientes:
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-03.js"
2. init-session action in progress ---------------------------------------------
3. success ---------------------------------------------
4. response= { action: 'init-session',
5. 'status': 700,
6. 'response': 'session started with type [json]' }
7. authenticate-user action in progress ---------------------------------------------
8. success ---------------------------------------------
9. response = { action: 'authenticate-user',
10. 'status': 103,
11. 'response':
12. [ 'No session currently active. Start with action [init-session]' ] }
13.
14. [Done] exited with code=0 in 0.834 seconds
- líneas 9-12: falla la autenticación de usuario: el servidor no retuvo el hecho de que se había iniciado una sesión JSON. Esto se debe a que la cookie de sesión enviada en respuesta a la primera [init-session] petición no se devolvió;
12.8. script [axios-04]
El [axios-04] script introduce dos mejoras en el [axios-03] script:
- maneja la cookie de sesión;
- factoriza en una [getRemoteData] función lo que es común a las [initSession] y [authenticateUser] funciones;
1. 'use strict';
2. import axios from 'axios';
3. import qs from 'qs'
4.
5. // Axios configuration
6. axios.defaults.timeout = 2000;
7. axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
8.
9. // session cookie
10. const sessionCookieName = "PHPSESSID";
11. let sessionCookie = '';
12.
13. // initialize session
14. async function initSession(axios) {
15. // HTTP request options [get /main.php?action=init-session&type=json]
16. const options = {
17. method: "GET",
18. // URL parameters
19. params: {
20. action: 'init-session',
21. type: 'json'
22. }
23. };
24. // Execute the HTTP request
25. return await getRemoteData(axios, options);
26. }
27.
28. async function authenticateUser(axios, user, password) {
29. // HTTP request options [post /main.php?action=authenticate-user]
30. const options = {
31. method: "POST",
32. headers: {
33. 'Content-type': 'application/x-www-form-urlencoded',
34. },
35. // POST body
36. data: qs.stringify({
37. user: user,
38. password: password
39. }),
40. // URL parameters
41. params: {
42. action: 'authenticate-user'
43. }
44. };
45. // execute the HTTP request
46. return await getRemoteData(axios, options);
47. }
48.
49. async function getRemoteData(axios, options) {
50. // for the session cookie
51. if (!options.headers) {
52. options.headers = {};
53. }
54. options.headers.Cookie = sessionCookie;
55. // Execute the HTTP request
56. let response;
57. try {
58. // asynchronous request
59. response = await axios.request('main.php', options);
60. } catch (error) {
61. // The [error] parameter is an exception instance—it can take various forms
62. if (error.response) {
63. // the server's response is in [error.response]
64. response = error.response;
65. } else {
66. // we re-throw the error
67. throw error;
68. }
69. }
70. // response is the entire HTTP response from the server (HTTP headers + the response itself)
71. // retrieve the session cookie if it exists
72. const setCookie = response.headers['set-cookie'];
73. if (setCookie) {
74. // setCookie is an array
75. // we search for the session cookie in this array
76. let found = false;
77. let i = 0;
78. while (!found && i < setCookie.length) {
79. // search for the session cookie
80. const results = RegExp('^(' + sessionCookieName + '.+?);').exec(setCookie[i]);
81. if (results) {
82. // store the session cookie
83. // eslint-disable-next-line require-atomic-updates
84. sessionCookie = results[i];
85. // found
86. found = true;
87. } else {
88. // next element
89. i++;
90. }
91. }
92. }
93. // The server's response is in [response.data]
94. return response.data;
95. }
96.
97. // The main function executes the asynchronous functions one by one
98. async function main() {
99. try {
100. // init-session
101. console.log("init-session action in progress ---------------------------------------------");
102. const response1 = await initSession(axios);
103. console.log("success ---------------------------------------------");
104. console.log("response=", response1);
105. // authenticate-user
106. console.log("authenticate-user action in progress ---------------------------------------------");
107. const response2 = await authenticateUser(axios, 'admin', 'admin');
108. console.log("success ---------------------------------------------");
109. console.log("response=", response2)
110. } catch (error) {
111. console.log("error ---------------------------------------------");
112. console.log("error=", error.message);
113. }
114. }
115.
116. // test
117. main();
Comentarios
- líneas 14-26: la [initSession] función. Ahora simplemente prepara la petición HTTP para enviarla al servidor, pero no la ejecuta. Delega esta tarea en el [getRemoteDate] método de las líneas 49-95;
- líneas 28-47: la función [authentifierUtilisateur] sigue el mismo procedimiento;
- línea 49: la [getRemoteData] función recibe los dos datos que necesita para ejecutar una petición HTTP:
- [axios], el objeto responsable de enviar la solicitud y recibir la respuesta;
- [opciones], las opciones de configuración de la petición que se enviará al servidor;
- línea 59: ejecutando la petición y esperando su respuesta JSON;
- líneas 60-68: gestión de posibles excepciones;
- línea 64: recuperar la respuesta, que puede estar encapsulada en el objeto error;
- línea 67: si el servidor lanzó una excepción sin incluir la respuesta del servidor, entonces el error recibido se propaga al código de llamada;
- La función [getRemoteData] gestiona la cookie de sesión:
- la almacena en la [sessionCookie] variable (línea 11) cuando la recibe por primera vez;
- luego lo devuelve con cada nueva petición HTTP;
- líneas 72-92: [getRemoteData] analiza cada respuesta del servidor para determinar si envió la cabecera [Set-Cookie] HTTP. Sabemos que el servidor envía una cookie de sesión llamada [PHPSESSID] (línea 10). Esta es, por tanto, la cookie que buscamos (línea 10);
- línea 72: recuperamos las [Set-Cookie] cabeceras HTTP si existen (no se distingue entre mayúsculas y minúsculas). De hecho, puede haber múltiples [Set-Cookie] cabeceras, por lo que recuperamos una matriz;
- línea 73: si se ha recuperado un array de cookies;
- líneas 78-90: buscamos la cookie de sesión entre todas las cookies del array;
- línea 80: la expresión relacional utilizada para buscar la cookie de sesión en la cookie #i;
- línea 81: si la comparación devuelve resultados;
- línea 84: tenemos en resultados[1], el primer paréntesis del patrón de expresión relacional, es decir, (PHPSESSID=xxxx) hasta el paréntesis de cierre (no incluido) que finaliza la cookie de sesión;
- líneas 50-54: con cada solicitud, la cookie de sesión se incluye en las cabeceras HTTP de la solicitud. La primera vez, esta cookie está vacía y, por tanto, será ignorada por el servidor;
Los resultados de la ejecución son los siguientes:
1. [Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-04.js"
2. init-session action in progress ---------------------------------------------
3. success ---------------------------------------------
4. response= { action: 'init-session',
5. 'status': 700,
6. 'response': 'session started with type [json]' }
7. User authentication in progress ---------------------------------------------
8. success ---------------------------------------------
9. response = { action: 'authenticate-user',
10. 'status': 200,
11. 'response': 'Authentication successful [admin, admin]' }
12.
13. [Done] exited with code=0 in 0.982 seconds