Skip to content

12. Proyecto [vuejs-10]: plugin [dao], solicitudes asíncronas 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 asíncronas al servidor de cálculo de impuestos. Debemos instalar esta dependencia:

Image

  • en [4-5], la línea añadida 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 apartado [La classe Dao]. La reproducimos 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 solicitud 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 adoptar 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;
      }
    }
    // response es el conjunto de la respuesta HTTP del servidor (encabezados HTTP + la propia respuesta)
    // se recupera la cookie de sesión si existe
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie es una matriz
      // se busca la cookie de sesión en esta matriz
      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 almacena la cookie de sesión
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // la hemos encontrado
          trouvé = true;
        } else {
          // 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] solo utiliza el método asíncrono [initSession] de las líneas 18-30. Recordemos que la clase [Dao] se instancia con un parámetro [axios], línea 10, parámetro inicializado por el código llamante. Este código llamante será aquí el script [./main.js].

12.3. El plugin [pluginDao]

El plugin [pluginDao] es el siguiente:


export default {
  install(Vue, dao) {
    // añade 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 plugin [event-bus], vemos que el plugin [pluginDao] crea en la clase/función [Vue] una nueva propiedad llamada [$dao]. Esta propiedad tendrá (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;
  • integra 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 realiza 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 plugin [pluginDao]
    • línea 24: importación del plugin;
    • 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 este motivo, la propiedad [Vue.$dao] designará la capa [dao] en todas las instancias de la clase/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 solicitud 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;
      },
      // gestión de errores
      doSomethingWithError(error) {
        // se indica que se ha producido 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á a punto de realizarse. Se debe mostrar un mensaje de espera al usuario;
    • [endWaiting]: la solicitud ha finalizado. Hay que detener la espera;
    • [error]: la solicitud ha fallado. Se debe mostrar un mensaje de error;
  • líneas 10-13: la alerta que muestra el posible mensaje de error. Está controlada por el booleano [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 el valor booleano [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 sea visible tras 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 guardan 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 señala 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 error
        .catch(error => {
          // se reenvía el error al componente principal
          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, solo en caso de que la consulta HTTP se haya realizado 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 ha creado el componente. Por lo tanto, se coloca su código en el método [created] de la línea 31;
  • línea 35: se indica al componente padre que la consulta asíncrona va a 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 solicita 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 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 indica al componente principal 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 marcha, 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.