Skip to content

12. Proyecto [vuejs-10]: complemento [dao], solicitudes asincrónicas HTTP

La estructura del proyecto [vuejs-10] es la siguiente:

Image

El proyecto [vuejs-10] muestra un componente que realiza una solicitud HTTP a un servidor remoto. La arquitectura utilizada es la siguiente:

Image

Un componente [Vue.js] utiliza la capa [dao] para comunicarse con el servidor de cálculo de impuestos.

12.1. Instalación de las dependencias

La aplicación [vuejs-10] utiliza la biblioteca [axios] para realizar las consultas asincrónicas al servidor de cálculo de impuestos. Debemos instalar esta dependencia:

Image

  • en [4-5], la línea agregada al archivo [package.json] tras la instalación de la biblioteca [axios] [1-3];

12.2. La clase [Dao]

La clase [Dao] es la que se desarrolló en el párrafo [La classe Dao]. La volvemos a presentar aquí a modo de recordatorio:


'use strict';

// importaciones
import qs from 'qs'

// clase [Dao]
class Dao {

  // constructor
  constructor(axios) {
    this.axios = axios;
    // cookie de sesión
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
  }

  // iniciar sesión
  async  initSession() {
    // opciones de la solicitud HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      // parámetros de la consulta URL
      params: {
        action: 'init-session',
        type: 'json'
      }
    };
    // ejecución de la consulta HTTP
    return await this.getRemoteData(options);
  }

  async  authentifierUtilisateur(user, password) {
    // opciones de la solicitud HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        '«Content-type»: «application/x-www-form-urlencoded»,
      },
      // cuerpo del POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      // parámetros de la URL
      params: {
        action: 'authentifier-utilisateur'
      }
    };
    // ejecución de la consulta HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // opciones de la consulta HHTP  [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      // parámetros de la consulta URL
      params: {
        action: 'get-admindata'
      }
    };
    // ejecución de la consulta HTTP
    const data = await this.getRemoteData(options);
    // resultado
    return data;
  }

  async  getRemoteData(options) {
    // para la cookie de sesión
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    // ejecución de la consulta HTTP
    let response;
    try {
      // solicitud asíncrona
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // el parámetro [error] es una instancia de excepción; puede presentar diversas formas
      if (error.response) {
        // la respuesta del servidor se encuentra en [error.response]
        response = error.response;
      } else {
        // Se vuelve a generar el error
        throw error;
      }
    }
    // la respuesta es el conjunto completo de la respuesta HTTP del servidor (encabezados HTTP + la respuesta en sí)
    // se recupera la cookie de sesión, si existe
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie es un array
      // se busca la cookie de sesión en esta tabla
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        // se busca la cookie de sesión
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // se guarda la cookie de sesión
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // la encontramos
          trouvé = true;
        } else {
          // el siguiente elemento
          i++;
        }
      }
    }
    // la respuesta del servidor está en [response.data]
    return response.data;
  }
}

// exportación de la clase
export default Dao;

El proyecto [vuejs-10] utiliza únicamente el método asíncrono [initSession] de las líneas 18 a 30. Cabe recordar que la clase [Dao] se instancia con un parámetro [axios], en la línea 10, parámetro inicializado por el código que la invoca. Este código que la invoca será, en este caso, el script [./main.js].

12.3. El complemento [pluginDao]

El complemento [pluginDao] es el siguiente:


export default {
  install(Vue, dao) {
    // agrega una propiedad [$dao] a la clase Vue
    Object.defineProperty(Vue.prototype, '$dao', {
      // cuando se hace referencia a Vue.$dao, se devuelve el segundo parámetro [dao]
      get: () => dao,
    })
  }
}

Si recordamos la explicación dada para el complemento [event-bus], vemos que el complemento [pluginDao] crea, en la clase o función [Vue], una nueva propiedad llamada [$dao]. Esta propiedad tendrá (aún queda por demostrar) como valor el objeto exportado por el script [./Dao], es decir, la clase [Dao] anterior.

12.4. El script principal [main.js]

El código del script principal [main.js] es el siguiente:


// importaciones
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';

// plugins
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);

// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// capa [dao]
import Dao from './Dao';
// configuración de Axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impuestos/versión-14';
axios.defaults.withCredentials = true;
// instanciación de la capa [dao]
const dao = new Dao(axios);

// complemento [dao]
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)

// configuración
Vue.config.productionTip = false

// instanciación del proyecto [App]
new Vue({
  render: h => h(App),
}).$mount('#app')

El script [main.js]:

  • instancia la capa [dao] en las líneas 14-21;
  • incorpora el complemento [pluginDao] en las líneas 24-25;
  • línea 15: se importa la clase [Dao];
  • líneas 17-18: se configura el objeto [axios] que ejecuta las consultas HTTP. Este objeto se importa en la línea 4;
    • línea 17: definición de un [timeout] de 2 segundos;
    • línea 18: el URL del servidor de cálculo de impuestos;
    • línea 19: para poder intercambiar cookies con el servidor;
  • líneas 24-25: uso del complemento [pluginDao]
    • línea 24: importación del complemento;
    • línea 25: integración del complemento. Se observa que el segundo parámetro del método [Vue.use] es la referencia de la capa [dao] definida en la línea 21. Por esta razón, la propiedad [Vue.$dao] hará referencia a la capa [dao] en todas las instancias de la clase o función [Vue], es decir, en todos los componentes [Vue.js];

12.5. La vista principal [App.vue]

El código de la vista principal [App] es el siguiente:


<template>
  <div class="container">
    <b-card>
      <!-- mensaje -->
      <b-alert show variant="success" align="center">
        <h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
      </b-alert>
      <!-- componente que realiza una consulta asíncrona al servidor de cálculo de impuestos-->
      <Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
      <!-- visualización de un posible error -->
      <b-alert show
               variant="danger"
               v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
      <!-- mensaje de espera con un indicador de carga -->
      <b-alert show v-if="showWaiting" variant="light">
        <strong>Requête au serveur de calcul d'impôt en cours...</strong>
        <b-spinner variant="primary" label="Spinning"></b-spinner>
      </b-alert>
    </b-card>
  </div>
</template>

<script>
  import Component1 from "./components/Component1";
  export default {
    name: "app",
    // estado del componente
    data() {
      return {
        // controla el indicador de espera
        showWaiting: false,
        // controla la visualización del error
        showError: false,
        // el error interceptado
        error: {}
      };
    },
    // componentes utilizados
    components: {
      Component1
    },
    // métodos de gestión de eventos
    methods: {
      // inicio de la espera
      beginWaiting() {
        // se muestra la espera
        this.showWaiting = true;
        // se oculta el mensaje de error
        this.showError = false;
      },
      // fin de la espera
      endWaiting() {
        // se oculta la espera
        this.showWaiting = false;
      },
      // manejo de errores
      doSomethingWithError(error) {
        // se indica que hubo un error
        this.error = error;
        // se muestra el mensaje de error
        this.showError = true;
      }
    }
  };
</script>

Comentarios

  • línea 9: [Component1] es el componente que realiza la consulta asíncrona HTTP. Puede emitir tres eventos:
    • [beginWaiting]: la consulta está por realizarse. Se debe mostrar un mensaje de espera al usuario;
    • [endWaiting]: la consulta ha finalizado. Se debe detener la espera;
    • [error]: la consulta falló. Se debe mostrar un mensaje de error;
  • líneas 10-13: la alerta que muestra el posible mensaje de error. Está controlada por la variable booleana [showError] de la línea 33. Muestra el error de la línea 35;
  • líneas 14-18: la alerta que muestra el mensaje de espera con un indicador de carga. Está controlada por la variable booleana [showWaiting] de la línea 47;
  • líneas 45-50: [beginWaiting] es el método que se ejecuta al recibir el evento [beginWaiting]. Muestra el mensaje de espera (línea 47) y oculta el mensaje de error (línea 49) en caso de que este último esté visible como resultado de una operación anterior;
  • líneas 52-55: [endWaiting] es el método que se ejecuta al recibir el evento [endWaiting]. Oculta el mensaje de espera (línea 54);
  • líneas 57-62: [doSomethingWithError] es el método que se ejecuta al recibir el evento [error]. Registra el error recibido (línea 59) y muestra el mensaje de error (línea 61);

12.6. El componente [Component1]

El código del componente [Component1] es el siguiente:


<template>
  <b-row>
    <b-col>
      <b-alert show
               variant="warning"
               v-if="showMsg">Valeur reçue du serveur = {{data}}</b-alert>
    </b-col>
  </b-row>
</template>

<script>
  export default {
    name: "component1",
    // estado del componente
    data() {
      return {
        showMsg: false
      };
    },
    // métodos de gestión de eventos
    methods: {
      // procesamiento de los datos recibidos del servidor
      doSomethingWithData(data) {
        // se registran los datos recibidos
        this.data = data;
        // se muestran
        this.showMsg = true;
      }
    },
    // se acaba de crear el componente
    created() {
      // se inicializa la sesión con el servidor: solicitud asíncrona
      // se utiliza la promesa devuelta por los métodos de la capa [dao]
      // se notifica el inicio de la operación
      this.$emit("beginWaiting");
      // se inicia la operación asíncrona
      this.$dao
        // se trata de inicializar una sesión jSON con el servidor de cálculo de impuestos
        .initSession()
        // método que procesa los datos recibidos en caso de éxito
        .then(data => {
          // se procesan los datos recibidos
          this.doSomethingWithData(data);
        })
        // método que procesa el error en caso de que se produzca
        .catch(error => {
          // se reporta el error al componente padre
          this.$emit("error", error.message);
        }).finally(() => {
          // fin de la espera
          this.$emit("endWaiting");
        })
    }
  };
</script>

Comentarios

  • líneas 4-6: el componente consta de una única alerta que muestra el valor devuelto por el servidor de cálculo de impuestos, pero solo si la consulta HTTP se realiza con éxito. Esta alerta está controlada por el valor booleano [showMsg] de la línea 17;
  • líneas 31-53: la consulta HTTP se realiza tan pronto como se crea el componente. Por lo tanto, su código se coloca en el método [created] de la línea 31;
  • línea 35: se le indica al componente padre que la consulta asíncrona está a punto de iniciarse;
  • líneas 37-39: se ejecuta el método [this.$dao.initSession]. Este inicia una sesión jSON con el servidor de cálculo de impuestos. El resultado inmediato de este método es un [Promise];
  • líneas 41-44: este código se ejecuta cuando el servidor ha devuelto su resultado sin errores. El resultado del servidor se encuentra en [data]. En la línea 43, se le pide al método [doSomethingWithData] que procese este resultado;
  • líneas 46-49: este código se ejecuta en caso de error durante la ejecución de la consulta. En la línea 48, se le indica al componente padre que se ha producido un error y se le pasa el mensaje de error [error.message];
  • líneas 49-52: este código se ejecuta en todos los casos. Se le indica al componente padre que la solicitud HTTP ha finalizado;
  • líneas 23-28: el método [doSomethingWithData] es el encargado de procesar los datos [data] enviados por el servidor. En la línea 25, se registran estos datos y en la línea 27 se muestran;

12.7. Ejecución del proyecto

Image

Si al iniciar el proyecto el servidor de cálculo de impuestos no está en ejecución, se obtiene el siguiente resultado:

Image

Iniciemos el servidor [Laragon] (véase https://tahe.developpez.com/tutoriels-cours/php7) y recarguemos la página anterior. El resultado es entonces el siguiente:

Image

Nota: aquí utilizamos la versión 14 del servidor de cálculo de impuestos definida en https://tahe.developpez.com/tutoriels-cours/php7.