Skip to content

12. Projekt [vuejs-10]: wtyczka [dao], asynchroniczne żądania HTTP

Struktura drzewa projektu [vuejs-10] wygląda następująco:

Image

Projekt [vuejs-10] przedstawia komponent wysyłający żądanie HTTP do serwera zdalnego. Zastosowana architektura wygląda następująco:

Image

Komponent [Vue.js] wykorzystuje warstwę [dao] do komunikacji z serwerem obliczającym podatek.

12.1. Instalacja zależności

Aplikacja [vuejs-10] wykorzystuje bibliotekę [axios] do wysyłania asynchronicznych żądań do serwera obliczającego podatek. Należy zainstalować tę zależność:

Image

  • w pliku [4-5], wiersz dodany do pliku [package.json] po zainstalowaniu biblioteki [axios] [1-3];

12.2. Klasa [Dao]

Klasa [Dao] to ta, która została opracowana w akapicie dotyczącym klasy [La classe Dao]. Przytaczamy ją tutaj dla przypomnienia:


'use strict';

// importy
import qs from 'qs'

// klasa [Dao]
class Dao {

  // konstruktor
  constructor(axios) {
    this.axios = axios;
    // plik cookie sesji
    this.sessionCookieName = "PHPSESSID";
    this.sessionCookie = '';
  }

  // inicjowanie sesji
  async  initSession() {
    // opcje żądania HHTP [get /main.php?action=init-session&type=json]
    const options = {
      method: "GET",
      // parametry URL
      params: {
        action: 'init-session',
        type: 'json'
      }
    };
    // wykonanie zapytania HTTP
    return await this.getRemoteData(options);
  }

  async  authentifierUtilisateur(user, password) {
    // opcje zapytania HHTP [post /main.php?action=authentifier-utilisateur]
    const options = {
      method: "POST",
      headers: {
        'Content-type': 'application/x-www-form-urlencoded',
      },
      // treść POST
      data: qs.stringify({
        user: user,
        password: password
      }),
      // parametry URL
      params: {
        action: 'authentifier-utilisateur'
      }
    };
    // wykonanie zapytania HTTP
    return await this.getRemoteData(options);
  }

  async getAdminData() {
    // opcje zapytania HHTP  [get /main.php?action=get-admindata]
    const options = {
      method: "GET",
      // parametry zapytania URL
      params: {
        action: 'get-admindata'
      }
    };
    // wykonanie zapytania HTTP
    const data = await this.getRemoteData(options);
    // wynik
    return data;
  }

  async  getRemoteData(options) {
    // dla pliku cookie sesji
    if (!options.headers) {
      options.headers = {};
    }
    options.headers.Cookie = this.sessionCookie;
    // wykonanie zapytania HTTP
    let response;
    try {
      // żądanie asynchroniczne
      response = await this.axios.request('main.php', options);
    } catch (error) {
      // parametr [error] jest instancją wyjątku – może przybierać różne formy
      if (error.response) {
        // odpowiedź serwera znajduje się w [error.response]
        response = error.response;
      } else {
        // błąd jest ponownie generowany
        throw error;
      }
    }
    // odpowiedź to cała odpowiedź serwera w formacie HTTP (nagłówki HTTP + sama odpowiedź)
    // pobieramy plik cookie sesji, jeśli istnieje
    const setCookie = response.headers['set-cookie'];
    if (setCookie) {
      // setCookie to tablica
      // w tej tablicy szuka się pliku cookie sesji
      let trouvé = false;
      let i = 0;
      while (!trouvé && i < setCookie.length) {
        // szukamy pliku cookie sesji
        const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
        if (results) {
          // zapisujemy plik cookie sesji
          // eslint-disable-next-line require-atomic-updates
          this.sessionCookie = results[1];
          // znaleziono
          trouvé = true;
        } else {
          // kolejny element
          i++;
        }
      }
    }
    // odpowiedź serwera znajduje się w [response.data]
    return response.data;
  }
}

// eksport klasy
export default Dao;

Projekt [vuejs-10] wykorzystuje wyłącznie metodę asynchroniczną [initSession] z wierszy 18–30. Przypominamy, że klasa [Dao] jest instancjonowana z parametrem [axios] (wiersz 10), który jest inicjowany przez kod wywołujący. Kodem wywołującym będzie w tym przypadku skrypt [./main.js].

12.3. Wtyczka [pluginDao]

Wtyczka [pluginDao] ma następującą postać:


export default {
  install(Vue, dao) {
    // dodaje właściwość [$dao] do klasy Vue
    Object.defineProperty(Vue.prototype, '$dao', {
      // gdy odwołuje się do Vue.$dao, zwracany jest drugi parametr [dao]
      get: () => dao,
    })
  }
}

Jeśli przypomnimy sobie wyjaśnienie podane dla wtyczki [event-bus], widzimy, że wtyczka [pluginDao] tworzy w klasie / funkcji [Vue] nową właściwość o nazwie [$dao]. Wartością tej właściwości będzie (co pozostaje jeszcze do wykazania) obiekt eksportowany przez skrypt [./Dao], czyli poprzednia klasa [Dao].

12.4. Główny skrypt [main.js]

Kod głównego skryptu [main.js] jest następujący:


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

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

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

// warstwa [dao]
import Dao from './Dao';
// konfiguracja axios
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/podatki/wersja-14';
axios.defaults.withCredentials = true;
// instancja warstwy [dao]
const dao = new Dao(axios);

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

// konfiguracja
Vue.config.productionTip = false

// instancja projektu [App]
new Vue({
  render: h => h(App),
}).$mount('#aplikacja')

Skrypt [main.js]:

  • instancjonuje warstwę [dao] w wierszach 14–21;
  • dołącza wtyczkę [pluginDao] w wierszach 24–25;
  • w wierszu 15: importowana jest klasa [Dao];
  • wiersze 17–18: konfiguruje się obiekt [axios], który realizuje zapytania HTTP. Obiekt ten jest importowany w wierszu 4;
    • wiersz 17: definiuje się obiekt [timeout] o czasie trwania 2 sekund;
    • wiersz 18: obiekt URL serwera obliczającego podatek;
    • wiersz 19: umożliwia wymianę plików cookie z serwerem;
  • wiersze 24–25: użycie wtyczki [pluginDao]
    • wiersz 24: import wtyczki;
    • wiersz 25: integracja wtyczki. Widać, że drugim parametrem metody [Vue.use] jest odwołanie do warstwy [dao] zdefiniowanej w wierszu 21. Z tego powodu właściwość [Vue.$dao] będzie odnosić się do warstwy [dao] we wszystkich instancjach klasy / funkcji [Vue], tj. we wszystkich komponentach [Vue.js];

12.5. Widok główny [App.vue]

Kod widoku głównego [App] jest następujący:


<template>
  <div class="container">
    <b-card>
      <!-- komunikat -->
      <b-alert show variant="success" align="center">
        <h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
      </b-alert>
      <!-- komponent wysyłający asynchroniczne zapytanie do serwera obliczającego podatek-->
      <Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
      <!-- wyświetlanie ewentualnego błędu -->
      <b-alert show
               variant="danger"
               v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
      <!-- komunikat o oczekiwaniu z ikoną ładowania -->
      <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",
    // stan komponentu
    data() {
      return {
        // sterowanie ikoną ładowania
        showWaiting: false,
        // sterowanie wyświetlaniem błędu
        showError: false,
        // przechwycony błąd
        error: {}
      };
    },
    // użyte komponenty
    components: {
      Component1
    },
    // metody zarządzania zdarzeniami
    methods: {
      // początek oczekiwania
      beginWaiting() {
        // wyświetlanie stanu oczekiwania
        this.showWaiting = true;
        // ukrywanie komunikatu o błędzie
        this.showError = false;
      },
      // koniec oczekiwania
      endWaiting() {
        // ukrywanie oczekiwania
        this.showWaiting = false;
      },
      // obsługa błędu
      doSomethingWithError(error) {
        // odnotowuje się wystąpienie błędu
        this.error = error;
        // wyświetlanie komunikatu o błędzie
        this.showError = true;
      }
    }
  };
</script>

Komentarze

  • wiersz 9: [Component1] to komponent, który wysyła asynchroniczne zapytanie HTTP. Może on generować trzy zdarzenia:
    • [beginWaiting]: żądanie zostanie wysłane. Należy wyświetlić użytkownikowi komunikat o oczekiwaniu;
    • [endWaiting]: żądanie zostało zakończone. Należy zakończyć oczekiwanie;
    • [error]: żądanie zakończyło się niepowodzeniem. Należy wyświetlić komunikat o błędzie;
  • wiersze 10–13: alert wyświetlający ewentualny komunikat o błędzie. Jest on sterowany przez zmienną logiczną [showError] z wiersza 33. Wyświetla błąd z wiersza 35;
  • wiersze 14–18: alert wyświetlający komunikat o oczekiwaniu wraz z ikoną ładowania. Jest on sterowany przez zmienną logiczną [showWaiting] z wiersza 47;
  • wiersze 45–50: [beginWaiting] to metoda uruchamiana po otrzymaniu zdarzenia [beginWaiting]. Wyświetla komunikat o oczekiwaniu (wiersz 47) i ukrywa komunikat o błędzie (wiersz 49) na wypadek, gdyby był on widoczny w wyniku poprzedniej operacji;
  • wiersze 52–55: [endWaiting] to metoda uruchamiana po otrzymaniu zdarzenia [endWaiting]. Ukrywa ona komunikat oczekiwania (wiersz 54);
  • wiersze 57–62: [doSomethingWithError] to metoda uruchamiana po otrzymaniu zdarzenia [error]. Rejestruje ona otrzymany błąd (wiersz 59) i wyświetla komunikat o błędzie (wiersz 61);

12.6. Komponent [Component1]

Kod komponentu [Component1] jest następujący:


<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",
    // stan komponentu
    data() {
      return {
        showMsg: false
      };
    },
    // metody obsługi zdarzeń
    methods: {
      // przetwarzanie danych otrzymanych z serwera
      doSomethingWithData(data) {
        // zapisywanie otrzymanych danych
        this.data = data;
        // wyświetlanie danych
        this.showMsg = true;
      }
    },
    // komponent został właśnie utworzony
    created() {
      // inicjowanie sesji z serwerem – żądanie asynchroniczne
      // wykorzystuje się obietnicę zwróconą przez metody warstwy [dao]
      // zgłaszany jest początek operacji
      this.$emit("beginWaiting");
      // uruchamia się operację asynchroniczną
      this.$dao
        // chodzi o zainicjowanie sesji jSON z serwerem obliczającym podatek
        .initSession()
        // metoda przetwarzająca otrzymane dane w przypadku powodzenia
        .then(data => {
          // przetwarzanie otrzymanych danych
          this.doSomethingWithData(data);
        })
        // metoda obsługująca błąd w przypadku niepowodzenia
        .catch(error => {
          // przekazywanie błędu do komponentu nadrzędnego
          this.$emit("error", error.message);
        }).finally(() => {
          // koniec oczekiwania
          this.$emit("endWaiting");
        })
    }
  };
</script>

Uwagi

  • wiersze 4–6: komponent składa się z jednego powiadomienia, które wyświetla wartość zwróconą przez serwer obliczający podatek, i to wyłącznie w przypadku pomyślnego zakończenia zapytania HTTP. Ten komunikat jest kontrolowany przez zmienną logiczną [showMsg] w wierszu 17;
  • wiersze 31–53: zapytanie HTTP jest wysyłane zaraz po utworzeniu komponentu. Jego kod umieszcza się zatem w metodzie [created] w wierszu 31;
  • wiersz 35: informujemy komponent nadrzędny, że zapytanie asynchroniczne zostanie uruchomione;
  • wiersze 37–39: wykonywana jest metoda [this.$dao.initSession]. Inicjuje ona sesję jSON z serwerem obliczającym podatek. Natychmiastowym wynikiem tej metody jest [Promise];
  • wiersze 41–44: ten kod jest wykonywany, gdy serwer zwrócił wynik bez błędu. Wynik z serwera znajduje się w [data]. W wierszu 43 wywołuje się metodę [doSomethingWithData] w celu przetworzenia tego wyniku;
  • wiersze 46–49: ten kod jest wykonywany w przypadku wystąpienia błędu podczas realizacji zapytania. W wierszu 48 informujemy komponent nadrzędny o wystąpieniu błędu i przekazujemy mu komunikat o błędzie [error.message];
  • wiersze 49–52: ten kod jest wykonywany w każdym przypadku. Informuje się komponent nadrzędny, że żądanie HTTP zostało zakończone;
  • wiersze 23–28: metoda [doSomethingWithData] służy do przetwarzania danych [data] przesłanych przez serwer. W wierszu 25 zapisujemy te dane, a w wierszu 27 je wyświetlamy;

12.7. Wykonanie projektu

Image

Jeśli podczas uruchamiania projektu serwer obliczający podatek nie jest uruchomiony, otrzymujemy następujący wynik:

Image

Uruchommy serwer [Laragon] (patrz https://tahe.developpez.com/tutoriels-cours/php7) i odświeżmy powyższą stronę. Wynik będzie wówczas następujący:

Image

Uwaga: korzystamy tutaj z wersji 14 serwera obliczającego podatek, zdefiniowanej na stronie https://tahe.developpez.com/tutoriels-cours/php7.