15. Przykład [nuxt-12]: żądania HTTP za pomocą axios
15.1. Présentation
W tym nowym przykładzie dowiemy się, jak w funkcjach [asyncData] można wysyłać żądania HTTP za pomocą biblioteki [axios]. Ponadto wykorzystamy już poznane pojęcia:
- korzystanie z wtyczek z przykładu [nuxt-06]:
- zapisanie stanu w pliku cookie sesji z przykładu [nuxt-06];
- kontrola nawigacji za pomocą middleware'ów z przykładu [nuxt-09];
- obsługa błędów z przykładu [nuxt-11];
Architektura przykładu będzie wyglądać następująco:

- aplikacja [nuxt] będzie przechowywana na serwerze [node.js] [3], pobrana przez przeglądarkę [1], która następnie ją uruchomi;
- zarówno klient [nuxt] [1], jak i serwer [nuxt] [3] będą wysyłać zapytania HTTP do serwera danych [2]. Serwer ten będzie serwerem obliczającym podatek, opracowanym w części PHP 7. Wykorzystamy jego najnowszą wersję, wersję 14, z autoryzowanymi zapytaniami CORS;
Architekturę przykładu można uprościć w następujący sposób:

- w [1] serwer [node.js] dostarcza strony [nuxt] do przeglądarki [2]. To właśnie warstwa serwera o numerach [web] i [8] dostarcza te strony. Aby dostarczyć stronę, serwer mógł zwrócić się o dane zewnętrzne do serwera danych o nazwie [3]. To warstwa [DAO] [9] wysyła niezbędne żądania HTTP;
- przy każdym wywołaniu strony na serwerze [node.js][1], przeglądarka [2] otrzymuje całą aplikację [nuxt], która następnie uruchamia się w trybie SPA. Blok [UI] (interfejs użytkownika) [4] wyświetla użytkownikowi strony [vue.js]. Działania użytkownika lub naturalny cykl życia stron mogą powodować wywołania danych zewnętrznych z serwera danych [3]. To właśnie warstwa [DAO] [5] wysyła wówczas niezbędne zapytania HTTP;
15.2. Struktura drzewa projektu

15.3. Plik konfiguracyjny [nuxt.config.js]
Projekt będzie sterowany przez następujący plik [nuxt.config.js]:
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Introduction à [nuxt.js]',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'ssr routing loading asyncdata middleware plugins store'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},
/*
** Customize the progress-bar color
*/
loading: false,
/*
** Global CSS
*/
css: [],
/*
** Plugins to load before mounting the App
*/
plugins: [
{ src: '@/plugins/client/plgSession', mode: 'client' },
{ src: '@/plugins/server/plgSession', mode: 'server' },
{ src: '@/plugins/client/plgDao', mode: 'client' },
{ src: '@/plugins/server/plgDao', mode: 'server' },
{ src: '@/plugins/client/plgEventBus', mode: 'client' }
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Dokumentacja: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// Dokumentacja: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Dokumentacja: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
// https://www.npmjs.com/package/cookie-universal-nuxt
'cookie-universal-nuxt'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) { }
},
// katalog kodu źródłowego
srcDir: 'nuxt-12',
// router
router: {
// katalog główny aplikacji URL
base: '/nuxt-12/',
// oprogramowanie pośredniczące routingu
middleware: ['routing']
},
// serwer
server: {
// port usługi, domyślnie 3000
port: 81,
// adresy sieciowe, na których nasłuchuje aplikacja, domyślnie localhost: 127.0.0.1
// 0.0.0.0 = wszystkie adresy sieciowe komputera
host: 'localhost'
},
// środowisko
env: {
// konfiguracja axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://„localhost/php7/scripts-web/impots/version-14”,
// konfiguracja pliku cookie sesji [nuxt]
maxAge: 60 * 5
}
}
- wiersz 22: samodzielnie zarządzamy alertem oczekiwania na zakończenie operacji asynchronicznej;
- wiersz 31: będziemy korzystać z różnych wtyczek, które będą przeznaczone albo dla klienta, albo dla serwera, ale nie dla obu jednocześnie;
- wiersz 52: moduł [axios] jest zintegrowany z modułem [nuxt]. Spowoduje to, że obiekt [axios], który będzie wysyłał żądania HTTP zaplikacji [nuxt] do serwera obliczającego podatek PHP, będzie dostępny w [context.$axios];
- wiersz 54: moduł [cookie-universal-nuxt] pozwoli nam zapisać sesję [nuxt] w pliku cookie;
- wiersz 60: właściwość [axios] pozwala nam skonfigurować moduł [@nuxtjs/axios] z wiersza 52. Nie skorzystamy z tej możliwości, preferując zamiast tego właściwość [env] z wiersza 88;
- wiersz 90: maksymalny czas oczekiwania na odpowiedź serwera obliczającego podatek;
- wiersz 91: wymagane dla klienta [nuxt] – zezwala na użycie plików cookie podczas komunikacji z serwerem obliczającym podatek;
- wiersz 92: podstawowy identyfikator URL serwera obliczającego podatek;
- wiersz 94: czas trwania sesji Nuxt (5 min);
- wiersz 77: nawigacja między klientem a serwerem [nuxt] będzie kontrolowana przez oprogramowanie pośredniczące do routingu;
15.4. Warstwa aplikacji [UI]

Zapewnimy aplikacji [nuxt] dostęp do serwera obliczającego podatek API za pośrednictwem następującego widoku:

- w [2] menu zapewniające dostęp do serwera obliczającego podatek API:
- [Authentification]: odpowiada stronie [authentification]. Strona ta wysyła żądanie uwierzytelnienia do serwera obliczającego podatek, używając identyfikatorów [admin, admin], które są obecnie jedynymi dozwolonymi. Wyświetlany wynik jest analogiczny do strony [3];
- [Requête AdminData]: odpowiada stronie [get-admindata]. Strona ta zwraca się do serwera obliczającego podatek o dane, określone tutaj jako [adminData], które umożliwiają obliczenie podatku. Wyświetlany wynik jest analogiczny do strony [3];
- [Fin session impôt]: odpowiada stronie [fin-session]. Strona ta wysyła do serwera obliczającego podatek żądanie zakończenia sesji PHP. Serwer anuluje wówczas bieżącą sesję PHP i inicjuje nową, pustą sesję;
15.5. Warstwy [dao] aplikacji [nuxt]
Jak wskazano powyżej, architektura aplikacji [nuxt] będzie wyglądać następująco:

- w [1] serwer [node.js] dostarcza strony [nuxt] do przeglądarki [2]. To warstwa serwera o numerach [web] i [8] dostarcza te strony. Aby dostarczyć stronę, serwer mógł zwrócić się o dane zewnętrzne do serwera danych o nazwie [3]. To warstwa [DAO] [9] wysyła niezbędne żądania HTTP;
- przy każdym wywołaniu strony na serwerze [node.js][1], przeglądarka [2] otrzymuje całą aplikację [nuxt], która następnie uruchamia się w trybie SPA. Blok [UI] (interfejs użytkownika) [4] wyświetla użytkownikowi strony [vue.js]. Działania użytkownika lub cykl życia stron mogą powodować wywołania danych zewnętrznych z serwera danych [3]. To właśnie warstwa [DAO] [5] wysyła wówczas niezbędne zapytania HTTP;
Będziemy korzystać z wersji 14 serwera obliczającego podatek opracowanego w dokumencie |Introduction au langage PHP7 par l’exemple|. Wykorzystamy jedynie część jego interfejsu programowania aplikacji (API) API:
Zapytanie | Odpowiedź |
| |
| |
| |
| |
15.5.1. Warstwa [dao] serwera [nuxt]

Serwer [node.js] [1] będzie korzystał z warstwy [dao] opisanej w dokumencie |Introduction au framework VUE.JS par l’exemple|. Przytaczamy tutaj jej kod:
'use strict';
// importy
import qs from 'qs'
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ź stanowi całość odpowiedzi 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 {
// następny element
i++;
}
}
}
// odpowiedź serwera znajduje się w [response.data]
return response.data;
}
}
// eksport klasy
export default Dao;
- wszystkie metody warstwy [dao] zwracają obiekt wysłany przez serwer danych [{action : ‘xx’, état : nn, réponse : {...}] wraz z:
- [action]: nazwa akcji wykonywanej przez serwer danych;
- [état]: wskaźnik numeryczny:
- [initSession]: status=700 dla odpowiedzi bez błędu;
- [authentifierUtilisateur]: status=200 dla odpowiedzi bez błędu;
- [getAdminData]: stan=1000 dla odpowiedzi bez błędu;
- [fin-session]: status=400 dla odpowiedzi bez błędu;
- [réponse]: odpowiedź powiązana z wskaźnikiem numerycznym [état]. Może się różnić w zależności od tego wskaźnika numerycznego;
Przyjrzyjmy się konstruktorowi klasy [Dao]:
// konstruktor
constructor(axios) {
this.axios = axios;
// plik cookie sesji
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- wiersz 2: obiekt [axios] przekazany jako argument do konstruktora jest dostarczany przez kod wywołujący. To właśnie on będzie wysyłał żądania HTTP;
- wiersz 5: nazwa pliku cookie sesji wysłanego przez serwer danych zapisana jako PHP;
- wiersz 6: plik cookie sesji, który jest wymieniany między warstwą [dao] a serwerem danych. Jest on inicjowany przez funkcję [getRemoteData] w wierszach 67–113;
W przypadku pliku cookie sesji należy uwzględnić dwie odrębne warstwy [dao]:
- warstwę przeglądarki;
- warstwę serwera;
Będziemy musieli zarządzać trzema plikami cookie sesji:
- ten wymieniany między klientem [nuxt] a serwerem PHP 7;
- ten wymieniany między serwerem [nuxt] a serwerem PHP 7;
- plik wymieniany między klientem [nuxt] a serwerem [nuxt];
Zadbamy o to, aby plik cookie sesji z serwerem PHP był taki sam dla klienta i serwera [nuxt]. Plik ten nazwiemy plikiem sesyjnym PHP. Jest to plik z przypadków 1 i 2. Plik z przypadku 3 nazwiemy plikiem sesyjnym [nuxt]. Będziemy więc mieli dwie sesje:
- sesja PHP z plikiem sesyjnym PHP;
- sesja [nuxt] z plikiem sesyjnym [nuxt];
Dlaczego używamy tego samego pliku cookie dla sesji klienta PHP i przeglądarki [nuxt]? Chcemy, aby aplikacja mogła komunikować się z serwerem PHP niezależnie od tego, czy jest to klient, czy serwer [nuxt]:
- jeśli akcja A serwera [nuxt] powoduje przejście serwera PHP do stanu E, stan ten jest odzwierciedlany w sesji PHP obsługiwanej przez serwer PHP;
- Korzystając z tego samego pliku cookie sesji PHP co serwer, akcja B klienta [nuxt], następująca po akcji A serwera [nuxt], odnalazłaby serwer PHP wstanie E pozostawionym przez serwer [nuxt] i mógłby w ten sposób wykorzystać pracę już wykonaną przez serwer [nuxt];
- jeśli po akcji B klienta [nuxt] nastąpi akcja C serwera [nuxt], to z tego samego powodu, co poprzednio, akcja ta będzie mogła opierać się na pracy wykonanej przez akcję B klienta [nuxt];
Aby przeglądarka klienta [nuxt] mogła komunikować się z serwerem PHP służącym do obliczania podatku, wykorzystamy wersję 14 tego serwera, która zezwala na wywołania międzydomenowe, tj. wywołania z przeglądarki do serwera PHP. Wywołania z serwera [nuxt] do serwera PHP nie są natomiast wywołaniami międzydomenowymi. Pojęcie to odnosi się wyłącznie do wywołań wykonywanych z przeglądarki.
Wróćmy do kodu konstruktora poprzedniej klasy [Dao]:
// konstruktor
constructor(axios) {
this.axios = axios;
// plik cookie sesji
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
- wiersze 5 i 6 odpowiadają plikowi cookie sesji PHP z serwerem obliczającym podatek;
Obsługa powyższego pliku cookie sesji PHP nie jest odpowiednia dla serwera [nuxt]: jego warstwa [dao] jest instancjonowana przy każdym nowym żądaniu kierowanym do serwera [nuxt]. Należy bowiem pamiętać, że wysłanie żądania o stronę do serwera [nuxt] oznacza zresetowanie aplikacji [nuxt]. Tak więc gdy po zakończeniu pierwszego żądania skierowanego do serwera danych przez serwer [nuxt] zainicjowany zostanie plik cookie sesji PHP warstwy [dao], wartość ta zostanie utracona podczas kolejnego żądania HTTP wysłanego przez ten sam serwer [nuxt], ponieważ w międzyczasie jego warstwa [dao] została odtworzona, konstruktor uruchomiono ponownie, a plik cookie sesji PHP zresetowano do pustego ciągu znaków (wiersz 6);
Rozwiązaniem jest użycie innego konstruktora dla warstwy [dao] serwera:
// konstruktor
constructor(axios, phpSessionCookie) {
// biblioteka axios
this.axios = axios
// wartość pliku cookie sesji
this.phpSessionCookie = phpSessionCookie
// nazwa pliku cookie sesji serwera PHP
this.phpSessionCookieName = 'PHPSESSID'
}
- wiersz 2: tym razem plik cookie sesji PHP zostanie przekazany do konstruktora warstwy [dao] serwera danych;
W jaki sposób serwer [nuxt] będzie mógł przekazać ten plik cookie sesji PHP konstruktorowi swojej warstwy [dao]? Zapiszemy plik sesyjny PHP w pliku sesyjnym [nuxt] wymienianym między przeglądarką a serwerem [nuxt]. Proces przebiega następująco:
- uruchamiana jest aplikacja [nuxt];
- gdy serwer [nuxt] wysyła swoje pierwsze żądanie HTTP do serwera PHP, zapisuje on plik cookie sesji PHP, który otrzymał, w pliku cookie sesji [nuxt], który wymienia z klientem [nuxt];
- przeglądarka obsługująca klienta [nuxt] odbiera ten plik sesyjny [nuxt] i w związku z tym systematycznie odsyła go przy każdym nowym żądaniu kierowanym do serwera [nuxt];
- gdy serwer [nuxt] będzie musiał wysłać nowe żądanie do serwera PHP, odnajdzie plik cookie sesji PHP w pliku cookie sesji [nuxt], który przeglądarka mu przesłała. Następnie prześle je do serwera PHP;
Istnieją dwa pliki cookie sesji i nie należy ich mylić:
- plik sesyjny [nuxt] wymieniany między serwerem [nuxt] a przeglądarką klienta [nuxt];
- plik sesyjny PHP wymieniany między serwerem [nuxt] a serwerem PHP lub między klientem [nuxt] a serwerem PHP;
Wróćmy teraz do kodu metody klasy [Dao]. Nie zawiera ona funkcji służącej do zamknięcia sesji PHP z serwerem obliczającym podatek. Dodajemy ją:
// koniec sesji obliczania podatku
async finSession() {
// opcje żądania HHTP [get /main.php?action=fin-session]
const options = {
method: 'GET',
// parametry zapytania URL
params: {
action: 'fin-session'
}
}
// wykonanie zapytania HTTP
const data = await this.getRemoteData(options)
// wynik
return data
}
Podczas testów okazuje się, że funkcja [getRemoteData] wywoływana w wierszu 12 nie jest odpowiednia dla metody [finSession]:
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ź stanowi całość odpowiedzi 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
// szukamy pliku cookie sesji w tej tabeli
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;
}
- wiersze 30–43: szukamy pliku cookie o nazwie [PHPSESSID=xxx]. Jeśli go znajdziemy, jest on zapisywany w klasie (wiersz 36);
Ten kod nie pasuje do nowej metody [finSession], ponieważ podczas akcji [fin-session] serwer PHP wysyła dwa pliki cookie o nazwie [PHPSESSID]. Oto przykład uzyskany przy użyciu klienta [Postman]:

- w [1] – żądanie klienta [Postman];
- w [3] – odpowiedź serwera PHP;
- w [4] – nagłówki HTTP z odpowiedzi serwera PHP;

- w [5] serwer PHP najpierw informuje, że usunął bieżącą sesję PHP;
- w [6] serwer PHP wysyła plik cookie nowej sesji PHP;
W obecnym kodzie funkcja [getRemoteData] pobiera plik cookie [5], podczas gdy należy zapamiętać plik cookie [6].
Należy zatem zmodyfikować kod funkcji [getRemoteData]:
async getRemoteData(options) {
// czy istnieje plik cookie sesji o nazwie PHP?
if (this.phpSessionCookie) {
// czy są nagłówki?
if (!options.headers) {
// tworzymy pusty obiekt
options.headers = {}
}
// nagłówek pliku cookie sesji PHP
options.headers.Cookie = this.phpSessionCookie
}
// wykonanie żądania 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ź stanowi całość odpowiedzi serwera HTTP (nagłówki HTTP + sama odpowiedź)
// szukamy pliku cookie sesji PHP wśród otrzymanych plików cookie
// wszystkie otrzymane pliki cookie
const cookies = response.headers['set-cookie']
if (cookies) {
// pliki cookie to tablica
// szukamy pliku cookie sesji PHP w tej tablicy
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// szukamy pliku cookie sesji o nazwie PHP
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// zapisujemy plik cookie sesji PHP
const phpSessionCookie = results[1]
// czy występuje w nim słowo [deleted]?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// mamy prawidłowy plik cookie sesji PHP
this.phpSessionCookie = phpSessionCookie
// znaleziono
trouvé = true
} else {
// następujący element
i++
}
} else {
// kolejny element
i++
}
}
}
// odpowiedź serwera znajduje się w [response.data]
return response.data
}
- wiersz 41: znaleziono plik cookie o nazwie [PHPSESSID]. Zapisujemy go lokalnie;
- wiersz 43: sprawdzamy, czy w zapisanym pliku cookie znajduje się ciąg znaków [PHPSESSID=deleted];
- wiersz 46: jeśli odpowiedź brzmi „nie”, oznacza to, że znaleziono właściwy plik cookie o nazwie [PHPSESSID]. Zapisujemy go w klasie;
Po wykonaniu funkcji [getRemoteData] plik cookie sesji PHP jest zapisywany w klasie, w zmiennej [this.phpSessionCookie]. Stwierdzono, że klasa jest instancjonowana przy każdym nowym żądaniu HTTP serwera [nuxt]. Plik cookie sesji PHP musi zatem zostać wyeksfiltrowany z klasy. W tym celu dodajemy do niej nową metodę:
// dostęp do pliku cookie sesji PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
- serwer [nuxt] żąda wykonania akcji od swojej warstwy [dao], przekazując plik cookie sesji PHP do jej konstruktora, o ile taki istnieje;
- po wykonaniu akcji serwer [nuxt] pobiera plik cookie sesji PHP zapisany przez warstwę [dao] za pomocą poprzedniej metody [getPhpSessionCookie]. Ten plik cookie może być taki sam jak poprzedni lub inny. Ten ostatni przypadek ma miejsce w dwóch sytuacjach:
- podczas wykonywania metody [initSession] (wcześniej nie istniał plik cookie sesji PHP);
- podczas wykonania metody [finSession] (serwer PHP zmienia plik cookie sesji PHP);
Warto zwrócić uwagę na pewną cechę charakterystyczną pliku cookie sesji PHP. Serwer [nuxt] nie zawsze otrzymuje ten plik cookie od serwera PHP. Serwer ten wysyła go bowiem tylko raz. Następnie już go nie wysyła. Analizując kod plików [getRemoteData] i [getPhpSessionCookie], można zauważyć, że gdy serwer PHP nie wysyła pliku cookie sesji, funkcja [getPhpSessionCookie] zwraca plik cookie sesji PHP przekazany do konstruktora. W ten sposób serwer zawsze wysyła do serwera PHP ostatni plik cookie sesji PHP, który ten mu przesłał.
15.5.2. Warstwa [dao] klienta [nuxt]

W przypadku klienta [nuxt], działającego w przeglądarce, wykorzystujemy kod klasy [Dao] z dokumentu |Introduction au framework VUE.JS par l’exemple|:
"use strict";
// importy
import qs from "qs";
class Dao {
// konstruktor
constructor(axios) {
this.axios = axios;
}
// 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ść zapytania 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) {
// wykonanie zapytania HTTP
let response;
try {
// zapytanie 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ź stanowi całość odpowiedzi serwera HTTP (nagłówki HTTP + sama odpowiedź)
// odpowiedź serwera znajduje się w [response.data]
return response.data;
}
}
// eksport klasy
export default Dao;
Kod ten różni się od warstwy [dao] serwera [nuxt] tym, że nie obsługuje pliku cookie sesji PHP w połączeniu z serwerem obliczającym podatek: zadanie to realizuje przeglądarka.
Podobnie jak w przypadku warstwy [dao] serwera [nuxt], dodamy metodę [finSession]:
// koniec sesji obliczania podatku
async finSession() {
// opcje zapytania HHTP [get /main.php?action=fin-session]
const options = {
method: 'GET',
// parametry zapytania URL
params: {
action: 'fin-session'
}
}
// wykonanie zapytania HTTP
const data = await this.getRemoteData(options)
// wynik
return data
}
Gdy klient [nuxt] wywołuje tę metodę, otrzymuje – podobnie jak serwer [nuxt] – dwa pliki cookie sesji PHP. W rzeczywistości to przeglądarka je odbiera i prawidłowo radzi sobie z tą sytuacją: zachowuje jedynie plik cookie nowej sesji PHP, zainicjowanej przez serwer obliczający podatek. Zatem przy kolejnej akcji klienta [nuxt] skierowanej do serwera PHP plik cookie sesji PHP będzie poprawny, ponieważ to przeglądarka go wysyła. Istnieje jednak problem: serwer [nuxt] nie wie o tym, że plik cookie sesji PHP uległ zmianie. W trakcie komunikacji z serwerem PHP klient wyśle zatem plik cookie sesji PHP, który już nie istnieje, co spowoduje problemy. Klient [nuxt] powinien powiadomić serwer [nuxt] o zmianie pliku cookie sesji PHP i przekazać mu ten plik. Wiemy, jak może to zrobić: za pośrednictwem pliku cookie sesji [nuxt], czyli pliku cookie wymienianego między klientem a serwerem [nuxt]. Klient [nuxt] ma co najmniej dwa sposoby na pobranie nowego pliku cookie sesji PHP:
- zwracając się o nie do przeglądarki;
- korzystając z metody [getRemoteData] serwera, który wie, jak pobrać nowy plik cookie sesji PHP;
Skorzystamy z drugiego rozwiązania, ponieważ jest ono już gotowe. Metoda [getRemoteData] klienta [nuxt] wygląda wówczas następująco:
async getRemoteData(options) {
// wykonanie zapytania HTTP
let response
try {
// zapytanie 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 HTTP (nagłówki HTTP + sama odpowiedź)
// szukamy pliku cookie sesji PHP wśród otrzymanych plików cookie
// wszystkie otrzymane pliki cookie
const cookies = response.headers['set-cookie']
if (cookies) {
// pliki cookie to tablica
// szukamy pliku cookie sesji PHP w tej tablicy
let trouvé = false
let i = 0
while (!trouvé && i < cookies.length) {
// szukamy pliku cookie sesji o nazwie PHP
const results = RegExp('^(' + this.phpSessionCookieName + '.+?)$').exec(cookies[i])
if (results) {
// zapisujemy plik cookie sesji o nazwie PHP
const phpSessionCookie = results[1]
// czy występuje w nim słowo [deleted]?
const results2 = RegExp(this.phpSessionCookieName + '=deleted').exec(phpSessionCookie)
if (!results2) {
// mamy prawidłowy plik cookie sesji PHP
this.phpSessionCookie = phpSessionCookie
// znaleziono
trouvé = true
} else {
// następujący element
i++
}
} else {
// kolejny element
i++
}
}
}
// odpowiedź serwera znajduje się w [response.data]
return response.data
}
W [getRemoteData] zachowaliśmy wyłącznie kod, który wykorzystuje odpowiedź serwera PHP w celu wyszukania pliku cookie sesji PHP. Nie zachowano kodu, który zawierał plik cookie sesji PHP w żądaniu wysyłanym do serwera PHP, ponieważ zadanie to realizuje przeglądarka, w której działa klient [nuxt].
Po uzyskaniu pliku cookie sesji PHP przez klienta [nuxt] należy umieścić go w sesji [nuxt], aby serwer [nuxt] mógł z niego skorzystać. Nie zajmuje się tym warstwa [dao], ale udostępnia ona za pomocą metody plik cookie sesji PHP, który zapamiętała:
// dostęp do pliku cookie sesji PHP
getPhpSessionCookie() {
return this.phpSessionCookie
}
Funkcja [getPhpSessionCookie] nie zawsze zwraca prawidłowy plik cookie sesji:
- należy pamiętać, że warstwa [dao] klienta [nuxt] jest trwała. Jest ona instancjonowana jednokrotnie, a następnie pozostaje w pamięci;
- dopóki serwer PHP nie wyśle pliku cookie sesji PHP do klienta [nuxt], funkcja [getPhpSessionCookie] klienta [nuxt] zwraca wartość [undefined];
- gdy serwer PHP wysyła plik cookie sesji PHP do klienta [nuxt], jest on zapisywany w [this.phpSessionCookie] i pozostanie tam, dopóki nie zostanie zastąpiony nowym plikiem sesyjnym PHP wysłanym przez serwer PHP. Funkcja [getPhpSessionCookie] klienta [nuxt] zwraca wówczas ostatni otrzymany plik cookie sesji PHP;
Warstwa [dao] klienta [nuxt] różni się od warstwy serwera [nuxt] tylko pod jednym względem: nie wysyła ona sama pliku cookie sesji PHP, ponieważ robi to przeglądarka. Niemniej jednak zdecydowano się zachować dwie odrębne warstwy [dao], ponieważ rozumowanie prowadzące do ich odpowiednich zapisów jest różne.
15.6. Sesja [nuxt]
![]()
Sesja [nuxt] (między klientem a serwerem Nuxt) zostanie zamknięta w następującym obiekcie [session]:
/* eslint-disable no-console */
// definicja sesji
const session = {
// zawartość sesji
value: {
// niezainicjowany store
initStoreDone: false,
// wartość magazynu Vuex
store: ''
},
// zapisanie sesji w pliku cookie
save(context) {
// zapisanie zmiennej sesyjnej w sesji
this.value.store = context.store.state
console.log('nuxt-session save=', this.value)
// zapisanie wartości sesji
context.app.$cookies.set('nuxt-session', this.value, { path: context.base, maxAge: context.env.maxAge })
},
// resetowanie sesji
reset(context) {
console.log('nuxt-session reset')
// resetowanie pamięci
context.store.commit('reset')
// zapisanie nowego magazynu w sesji i zapisanie sesji
this.save(context)
}
}
// eksport sesji
export default session
- wiersze 5–10: sesja posiada tylko jedną właściwość [value] z dwiema podwłaściwościami:
- [initStoreDone], która wskazuje, czy magazyn został zainicjowany, czy nie;
- [store]: wartość [store.state] magazynu Vuex aplikacji;
- wiersze 12–18: metoda [save] służy do zapisania sesji [nuxt] w pliku cookie. W tym miejscu wykorzystuje się bibliotekę [cookie-universal-nuxt] do zarządzania plikiem cookie. Należy zwrócić uwagę na nazwę pliku cookie sesji [nuxt]: [nuxt-session] (wiersz 17);
- wiersze 20–26: metoda [reset] resetuje sesję [nuxt];
- wiersz 23: statek Vuex jest resetowany, a następnie zapisywany w sesji w wierszu 25;
15.7. Wtyczki do zarządzania sesją [nuxt]

15.7.1. Wtyczka do zarządzania sesją [nuxt] z serveur [nuxt]
Po uruchomieniu aplikacji jako pierwszy uruchamia się serwer [nuxt]. To właśnie on zainicjuje sesję [nuxt]. Skrypt [server/plgSession] wygląda następująco:
/* wyłączenie reguły „no-console” w eslint */
// import sesji
import session from '@/entities/session'
export default (context, inject) => {
// zarządzanie sesją serwerową
console.log('[plugin server plgSession]')
// czy istnieje już sesja?
const value = context.app.$cookies.get('nuxt-session')
if (!value) {
// nowa sesja
console.log("[plugin server plgSession], démarrage d'une nouvelle session")
} else {
// istniejąca sesja
console.log("[plugin server plgSession], reprise d'une session existante")
session.value = value
}
// wstrzykujemy funkcję do [context, Vue], która ustawi bieżącą sesję
inject('session', () => session)
}
- wiersz 4: importujemy kod sesji [nuxt];
- wiersz 11: pobierana jest wartość pliku cookie sesji [nuxt];
- wiersze 12–15: jeśli plik cookie sesji [nuxt] nie istnieje, to sesja [nuxt] zaimportowana w wierszu 4 jest wystarczająca. Nie ma nic więcej do zrobienia;
- wiersze 15–19: jeśli plik cookie sesji [nuxt] istniał, to w wierszu 18 zapisujemy jego wartość w sesji zaimportowanej w wierszu 4;
- wiersz 22: sesja została zainicjowana lub przywrócona. Udostępniamy ją za pomocą funkcji [$session];
15.7.2. Wtyczka do zarządzania sesją [nuxt] dla klienta [nuxt]
Skrypt [client/plgSession] ma następującą postać:
/* eslint-disable no-console */
// import sesji
import session from '@/entities/session'
export default (context, inject) => {
// zarządzanie sesją klienta
console.log('[plugin client plgSession], reprise de la session [nuxt] du serveur')
// pobieramy istniejącą sesję z serwera Nuxt
session.value = context.app.$cookies.get('nuxt-session')
// wstrzykujemy do [context, Vue] funkcję, która ustawi bieżącą sesję
inject('session', () => session)
}
- wiersz 4: importowana jest sesja [nuxt];
- wiersz 10: pobierana jest bieżąca sesja [nuxt] z pliku cookie [nuxt-session];
- wiersz 13: zwracamy sesję [nuxt] zaimportowaną w wierszu 4 za pomocą wstrzykniętej funkcji [$session];
15.8. Wtyczki warstw [dao]

15.8.1. Wtyczka warstwy [dao] klienta [nuxt]
Skrypt [client/plgDao] ma następującą postać:
/* eslint-disable no-console */
// tworzymy punkt dostępu do warstwy [Dao]
import Dao from '@/api/client/Dao'
export default (context, inject) => {
// konfiguracja axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
context.$axios.defaults.withCredentials = context.env.withCredentials
// instancjonowanie warstwy [dao]
const dao = new Dao(context.$axios)
// wstrzyknięcie funkcji [$dao] do kontekstu
inject('dao', () => dao)
// log
console.log('[fonction client $dao créée]')
}
- wiersz 3: importowana jest warstwa [dao] klienta [nuxt];
- wiersze 6–8: konfiguruje sięobiekt [context.$axios], który będzie wysyłał zapytania HTTP z warstwy [dao] klienta [nuxt], wykorzystując informacje z pliku [nuxt.config]:
// środowisko
env: {
// konfiguracja axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// konfiguracja pliku cookie sesji [nuxt]
maxAge: 60 * 5
}
- wiersz 10: instancja warstwy [dao] klienta [nuxt] zostaje utworzona;
- wiersz 12: funkcja [$dao] jest wstrzykiwana do kontekstu i stron klienta. Funkcja ta zapewnia dostęp do warstwy [dao] z wiersza 10;
Należy zatem pamiętać, że aby uzyskać dostęp do warstwy [dao] klienta [nuxt] podczas jego działania, należy wpisać:
- [context.app.$dao()], jeśli kontekst jest znany;
- [this.$dao()] na stronie [Vue.js];
15.8.2. Wtyczka warstwy [dao] dla serveur [nuxt]
Skrypt [server/plgDao] wygląda następująco:
/* eslint-disable no-console */
// tworzymy punkt dostępu do warstwy [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// konfiguracja axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// pobieramy plik cookie sesji
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instancjonowanie warstwy [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// wstrzyknięcie funkcji [$dao] do kontekstu
inject('dao', () => dao)
// logowanie
console.log('[fonction server $dao créée]')
}
- wiersz 3: importowana jest warstwa [dao] z serwera [nuxt];
- wiersze 6–7: konfiguruje sięobiekt [context.$axios], który będzie wysyłał żądania HTTP z warstwy [dao] serwera [nuxt], wykorzystując informacje z pliku [nuxt.config]:
// środowisko
env: {
// konfiguracja axios
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// konfiguracja pliku cookie sesji [nuxt]
maxAge: 60 * 5
}
- wiersz 9: pobieramy magazyn aplikacji [nuxt];
- wiersz 10: jeśli sklep istnieje, pobieramy plik cookie sesji PHP, ponieważ jest on potrzebny do utworzenia instancji warstwy [dao] serwera [nuxt];
- wiersz 13: tworzymy instancję warstwy [dao] serwera [nuxt];
- wiersz 15: funkcja [$dao] jest wstrzykiwana do kontekstu i stron serwera [nuxt]. Funkcja ta zapewnia dostęp do warstwy [dao] z wiersza 13;
Należy zatem pamiętać, że aby uzyskać dostęp do warstwy [dao] serwera [nuxt] podczas jego wykonywania, należy napisać:
- [context.app.$dao()], jeśli kontekst jest znany;
- [this.$dao()] na stronie [Vue.js];
15.9. Magazyn Vuex
![]()
Magazyn [Vuex] będzie przechowywał wszystkie dane, które muszą być współdzielone przez różne komponenty aplikacji [pages, client, serveur], bez konieczności zapewniania reaktywności tych danych.
/* eslint-disable no-console */
// stan rolety
export const state = () => ({
// rozpoczęta sesja jSON
jsonSessionStarted: false,
// użytkownik uwierzytelniony
userAuthenticated: false,
// plik cookie sesji PHP
phpSessionCookie: '',
// adminData
adminData: ''
})
// zmiany w sklepie
export const mutations = {
// aktualizacja stanu
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
},
// reset pamięci
reset() {
this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
}
}
// operacje na pamięci
export const actions = {
nuxtServerInit(store, context) {
// kto wykonuje ten kod?
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// inicjowanie sesji
initStore(store, context)
}
}
function initStore(store, context) {
// pamięć to pamięć, którą należy zainicjować
// pobieramy sesję
const session = context.app.$session()
// czy sesja została już zainicjowana?
if (!session.value.initStoreDone) {
// uruchamiamy nowy obiekt przechowujący
console.log("nuxtServerInit, initialisation d'une nouvelle session")
// umieszczamy magazyn w sesji
session.value.store = store.state
// magazyn jest teraz zainicjowany
session.value.initStoreDone = true
} else {
console.log("nuxtServerInit, reprise d'un store existant")
// aktualizujemy magazyn danymi z magazynu sesji
store.commit('replace', session.value.store)
}
// zapisujemy sesję
session.save(context)
// log
console.log('initStore terminé, store=', store.state)
}
Dane przechowywane w magazynie to:
- wiersz 6: [jsonSessionStarted] zostanie ustawiony na wartość „prawda”, gdy tylko zakończy się pomyślnie inicjalizacja sesji jSON z serwerem PHP, niezależnie od tego, czy została ona przeprowadzona przez klienta, czy przez serwer [nuxt]. Po zakończeniu tej inicjalizacji plik cookie sesji z serwerem PHP zostanie pobrany i umieszczony we właściwości [phpSessionCookie], wiersz 10;
- w wierszu 8: [userAuthenticated] zostanie ustawione na „prawda”, gdy tylko uwierzytelnienie na serwerze PHP zakończy się powodzeniem, niezależnie od tego, czy zostało przeprowadzone przez klienta, czy przez serwer [nuxt];
- wiersz 12: [adminData] będzie wartością [adminData] uzyskaną od serwera PHP po pomyślnym uwierzytelnieniu;
- wiersze 18–22: operacja [replace] pozwala zainicjować poprzednie właściwości wartościami obiektu przekazanego jako parametr;
- wiersze 24–26: operacja [reset] przywraca właściwościom magazynu ich wartości początkowe;
- wiersze 31–37: funkcja [nuxtServerInit] przekazuje swoje zadanie funkcji [initStore];
- wiersze 39–60: funkcja [initStore] pełni dwie role:
- jeśli magazyn nie został zainicjowany, zostaje zainicjowany i umieszczony w sesji;
- jeśli store został już zainicjowany, jego wartość jest pobierana z sesji [nuxt];
- wiersz 42: pobierana jest sesja nuxt;
- wiersz 44: sprawdzamy, czy store został zainicjowany:
- jeśli nie, umieszczamy początkową wartość store’u w sesji (wiersz 48);
- następnie w wierszu 50 zaznacza się, że store został zainicjowany;
- wiersze 51–55: jeśli magazyn został zainicjowany, wykorzystuje się go w wierszu 54 do zainicjowania magazynu wartością zawartą w sesji;
- w wierszu 57: w każdym przypadku sesja jest zapisywana w pliku cookie o nazwie [nuxt-session] wraz z zawartą w niej zmienną;
15.10. Wtyczka [plgEventBus]

Wtyczka ta ma na celu udostępnienie magistrali zdarzeń klientowi [nuxt] za pośrednictwem funkcji [$eventBus] wstrzykniętej do kontekstu klienta [nuxt]. Nie ma sensu wstrzykiwać jej w kontekst serwera [nuxt], ponieważ nie obsługuje on zdarzeń. Niemniej jednak zauważyliśmy już, że wstrzyknięcie jej po stronie serwera, a następnie jej użycie nie powoduje błędu.
/* eslint-disable no-console */
// tworzy się magistralę zdarzeń między widokami
import Vue from 'vue'
export default (context, inject) => {
// magistrala zdarzeń
const eventBus = new Vue()
// wstrzyknięcie funkcji [$eventBus] do kontekstu
inject('eventBus', () => eventBus)
// log
console.log('[fonction $eventBus créée]')
}
Z tą wtyczką zetknęliśmy się już w akapicie dotyczącym linków. Funkcja [$eventBus] będzie dostępna po stronie klienta poprzez notacje:
- [context.app.$eventBus()] tam, gdzie dostępny jest kontekst;
- [this.$eventBus()] na stronach [Vue.js] po stronie klienta;
15.11. Komponenty aplikacji [nuxt]

Komponent [layout] to ten z poprzednich przykładów:
<!-- układ widoków -->
<template>
<!-- wiersz -->
<div>
<b-row>
<!-- obszar z trzema kolumnami -->
<b-col v-if="left" cols="3">
<slot name="left" />
</b-col>
<!-- obszar z dziewięcioma kolumnami -->
<b-col v-if="right" cols="9">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// parametry
props: {
left: {
type: Boolean
},
right: {
type: Boolean
}
}
}
</script>
Komponent [navigation] wygląda następująco:
<template>
<!-- menu Bootstrap z trzema opcjami -->
<b-nav vertical>
<b-nav-item to="/authentification" exact exact-active-class="active">
Authentification
</b-nav-item>
<b-nav-item to="/get-admindata" exact exact-active-class="active">
Requête AdminData
</b-nav-item>
<b-nav-item to="/fin-session" exact exact-active-class="active">
Fin session impôt
</b-nav-item>
</b-nav>
</template>
15.12. Układy aplikacji [nuxt]

15.12.1. [default]
Układ [default] jest tym samym, który został użyty w przykładzie [nuxt-11] w akapicie dotyczącym linku:
<template>
<div class="container">
<b-card>
<!-- komunikat -->
<b-alert show variant="success" align="center">
<h4>[nuxt-12] : requêtes HTTP avec axios</h4>
</b-alert>
<!-- aktualny widok routingu -->
<nuxt />
<!-- komunikat oczekiwania -->
<b-alert v-if="showLoading" show variant="light">
<strong>Requête au serveur de données en cours...</strong>
<div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
</b-alert>
<!-- błąd operacji asynchronicznej -->
<b-alert v-if="showErrorLoading" show variant="danger">
<strong>La requête au serveur de données a échoué : {{ errorLoadingMessage }}</strong>
</b-alert>
</b-card>
</div>
</template>
<script>
/* eslint-disable no-console */
export default {
name: 'App',
data() {
return {
showLoading: false,
showErrorLoading: false
}
},
// cykl życia
beforeCreate() {
console.log('[default beforeCreate]')
},
created() {
console.log('[default created]')
if (process.client) {
// słuchamy zdarzenia [loading]
this.$eventBus().$on('loading', this.mShowLoading)
// oraz zdarzenie [errorLoadingMessage]
this.$eventBus().$on('errorLoading', this.mShowErrorLoading)
}
},
beforeMount() {
console.log('[default beforeMount]')
},
mounted() {
console.log('[default mounted]')
},
methods: {
// obsługa komunikatu oczekującego
mShowLoading(value) {
console.log('[default mShowLoading], showLoading=', value)
this.showLoading = value
},
// błąd operacji asynchronicznej
mShowErrorLoading(value, errorLoadingMessage) {
console.log('[default mShowErrorLoading], showErrorLoading=', value, 'errorLoadingMessage=', errorLoadingMessage)
this.showErrorLoading = value
this.errorLoadingMessage = errorLoadingMessage
}
}
}
</script>
- wiersze 10–14: wyświetlają komunikat o oczekiwaniu na zakończenie operacji asynchronicznej klienta [nuxt];
- wiersze 15–18: wyświetlają ewentualny komunikat o błędzie operacji asynchronicznej;
- wiersz 37: funkcja [created] ze strony [default] jest wykonywana przed funkcją [mounted] ze stron;
- wiersz 39: jeśli wykonawcą jest klient [nuxt], wówczas strona [default] nasłuchuje zdarzeń:
- [loading], która sygnalizuje początek lub koniec oczekiwania. Następnie wykonywana jest funkcja [mShowLoading];
- [errorLoading], która sygnalizuje konieczność wyświetlenia komunikatu o błędzie. Następnie wykonywana jest funkcja [mShowErrorLoading];
- strony [nuxt]:
- wyświetlają komunikat oczekiwania, wysyłając zdarzenie [‘loading’, true] na magistralę zdarzeń;
- ukrywają komunikat oczekiwania, wysyłając zdarzenie [‘loading’, false] na magistralę zdarzeń;
- wyświetlają komunikat o błędzie, wysyłając zdarzenie [‘errorLoading’, true] na magistralę zdarzeń;
- ukrywają komunikat o błędzie, wysyłając zdarzenie [‘errorLoading’, false] na magistrali zdarzeń;
15.12.2. [error]
Układ [error] wyświetla komunikat o błędzie systemowym (nieobsługiwanym przez programistę):
<!-- definicja widoku HTML -->
<template>
<!-- układ strony -->
<Layout :left="true" :right="true">
<!-- alert w prawej kolumnie -->
<template slot="right">
<!-- komunikat na różowym tle -->
<b-alert show variant="danger" align="center">
<h4>L'erreur suivante s'est produite : {{ JSON.stringify(error) }}</h4>
</b-alert>
</template>
<!-- menu nawigacyjne w lewej kolumnie -->
<Navigation slot="left" />
</Layout>
</template>
<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */
import Layout from '@/components/layout'
import Navigation from '@/components/navigation'
export default {
name: 'Error',
// używane komponenty
components: {
Layout,
Navigation
},
// właściwość [props]
props: { error: { type: Object, default: () => 'waiting ...' } },
// cykl życia
beforeCreate() {
// klient i serwer
console.log('[error beforeCreate]')
},
created() {
// klient i serwer
console.log('[error created, error=]', this.error)
},
beforeMount() {
// tylko klient
console.log('[error beforeMount]')
},
mounted() {
// tylko klient
console.log('[error mounted]')
}
}
</script>
15.13. Strona [index] uruchamiana przez serwer [nuxt]

Strona [index.vue] ma tę cechę, że jest dostępna wyłącznie za pośrednictwem serwera [nuxt]. Użytkownikowi nie jest udostępniony żaden link umożliwiający dostęp do niej za pośrednictwem klienta [nuxt]. Jej kod jest następujący:
<!-- strona główna -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat-->
<b-alert slot="right" show variant="warning">Initialisation de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'InitSession',
// używane komponenty
components: {
Layout,
Navigation
},
// dane asynchroniczne
async asyncData(context) {
// log
console.log('[index asyncData started]')
try {
// rozpoczynamy sesję jSON
const dao = context.app.$dao()
const response = await dao.initSession()
// log
console.log('[index asyncData response=]', response)
// pobieranie pliku cookie sesji PHP do wykorzystania w kolejnych żądaniach
const phpSessionCookie = dao.getPhpSessionCookie()
// zapisujemy plik cookie sesji PHP w sesji [nuxt]
context.store.commit('replace', { phpSessionCookie })
// czy wystąpił błąd?
if (response.état !== 700) {
// błąd znajduje się w response.réponse
throw new Error(response.réponse)
}
// należy odnotować, że sesja jSON została uruchomiona
context.store.commit('replace', { jsonSessionStarted: true })
// zwracamy wynik
return { result: '[succès]' }
} catch (e) {
// log
console.log('[index asyncData error=]', e)
// odnotowano, że sesja jSON nie została uruchomiona
context.store.commit('replace', { jsonSessionStarted: false })
// zgłaszamy błąd
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// zapisujemy zawartość pamięci
const session = context.app.$session()
session.save(context)
// log
console.log('[index asyncData finished]')
}
},
// cykl życia
beforeCreate() {
console.log('[index beforeCreate]')
},
created() {
console.log('[index created]')
},
beforeMount() {
console.log('[index beforeMount]')
},
mounted() {
console.log('[index mounted]')
// tylko klient
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- wiersz 7: strona wyświetla wynik [result] z zapytania asynchronicznego (wiersze 46 i 51);
- wiersz 31: operacją asynchroniczną jest otwarcie sesji jSON z serwerem obliczającym podatek;
- wiersz 25: wiadomo, że gdy strona jest żądana bezpośrednio od serwera [nuxt], funkcja [asyncData] jest wykonywana wyłącznie przez serwer, a nie przez klienta [nuxt], który uruchamia się, gdy przeglądarka otrzyma odpowiedź od serwera [nuxt];
- wiersz 30: pobierana jest warstwa [dao] w kontekście serwera [nuxt];
- wiersz 35: jeśli serwer nie wysłał jeszcze żądania do serwera obliczającego podatek, otrzymuje swój pierwszy plik cookie sesji o nazwie PHP, w przeciwnym razie otrzymuje ostatni plik cookie sesji o nazwie PHP, który otrzymał (zobacz kod warstwy [dao] serwera [nuxt] w akapicie z linkiem);
- wiersz 37: zapisujemy ten plik sesyjny PHP w pamięci;
- wiersze 39–42: sprawdzamy, czy operacja zakończyła się powodzeniem. Jeśli nie, generowany jest wyjątek, który zostanie przechwycony przez [catch] w wierszu 47;
- wiersz 44: odnotowuje się w pamięci, że sesja jSON z serwerem PHP została uruchomiona;
- wiersz 46: zwracany jest wynik [result], który jest wyświetlany w wierszu 7;
- wiersze 47–54: przetwarzany jest ewentualny wyjątek. Może on mieć dwa rodzaje:
- operacja HTTP z wiersza 31 zakończyła się niepowodzeniem z powodu błędu komunikacyjnego między serwerem [nuxt] a serwerem PHP;
- operacja HTTP z wiersza 31 zakończyła się powodzeniem, ale otrzymany wynik zgłosił błąd (wiersze 39–42);
- wiersz 51: zauważamy, że sesja jSON z serwerem PHP nie została uruchomiona;
- wiersz 53: zwracany jest wynik [result], który jest wyświetlany w wierszu 7. Ponadto ustawia się właściwości [showErrorLoading] i [errorLoadingMessage], których klient [nuxt] użyje do wyświetlenia komunikatu o błędzie po otrzymaniu strony wysłanej przez serwer [nuxt] (wiersze 72–79);
- wiersze 54–60: kod wykonywany w każdym przypadku (niezależnie od powodzenia lub niepowodzenia);
- wiersz 56: pobieramy sesję [nuxt] w kontekście serwera [nuxt];
- wiersz 57: zapisuje się ją;
- wiersze 63–68: po zakończeniu działania funkcji [asyncData] serwer [nuxt] wykonuje funkcje [beforeCreate] i [create];
Uwaga: wykonanie strony [index] przez serwer [nuxt] może zakończyć się niepowodzeniem, na przykład jeśli serwer obliczający podatek nie jest uruchomiony w momencie uruchomienia aplikacji [nuxt]:

W takim przypadku jedynym rozwiązaniem jest uruchomienie serwera obliczającego podatek, a następnie samej aplikacji [nuxt], ponieważ menu nawigacyjne nie oferuje opcji zainicjowania sesji jSON z serwerem obliczającym podatek;
15.14. Strona [index] uruchamiana przez klienta [nuxt]
Strona [index] jest uruchamiana przez klienta [nuxt] dopiero po tym, jak serwer [nuxt] ją do niego wyśle. Serwer ten przesłał mu informacje [result] oraz ewentualnie [showErrorLoading] i [errorLoadingMessage].
Wiadomo, że funkcja [asyncData] nie zostanie wykonana. Pozostają zatem funkcje cyklu życia, a w szczególności funkcja [mounted]:
mounted() {
console.log('[index mounted]')
// tylko klient
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
- klient [nuxt] automatycznie włącza do właściwości strony elementy [result] oraz ewentualnie [showErrorLoading, errorLoadingMessage], które przesłał mu serwer [nuxt]:
- właściwość [result] jest wyświetlana w wierszu 7;
- właściwości [showErrorLoading, errorLoadingMessage] są wykorzystywane przez metodę [mounted]: w wierszu 4 sprawdzana jest właściwość [showErrorLoading]. Jeśli jest prawdziwa, w wierszu 6 wykorzystuje się magistralę zdarzeń klienta [nuxt], aby zasygnalizować, że należy wyświetlić komunikat o błędzie;
- zdarzenie [errorLoading] wywołane w wierszu 6 jest przechwytywane przez stronę [layouts/default] opisaną w akapicie „link”;
15.15. Strona [authentification] jest uruchamiana przez serwer [nuxt]
Strona [authentification] odpowiada za identyfikację użytkownika na serwerze obliczającym podatek. Jej kod jest następujący:
<!-- strona uwierzytelniania -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat-->
<b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Authentification',
// używane komponenty
components: {
Layout,
Navigation
},
// dane asynchroniczne
async asyncData(context) {
// log
console.log('[authentification asyncData started]')
if (process.client) {
// początek oczekiwania na klienta [nuxt]
context.app.$eventBus().$emit('loading', true)
// brak błędu
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// autoryzacja na serwerze
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// log
console.log('[authentification asyncData response=]', response)
// wynik
const userAuthenticated = response.état === 200
// odnotowuje się, czy użytkownik został uwierzytelniony, czy nie
context.store.commit('replace', { userAuthenticated })
// zapisywanie magazynu w sesji [nuxt]
const session = context.app.$session()
session.save(context)
// błąd uwierzytelniania?
if (!userAuthenticated) {
// błąd znajduje się w response.réponse
throw new Error(response.réponse)
}
// zwracany jest wynik
return { result: '[succès]' }
} catch (e) {
// zgłaszamy błąd
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[authentification asyncData finished]')
if (process.client) {
// koniec oczekiwania na klienta [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// cykl życia
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// tylko klient
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
- wiersz 7: strona wyświetla wynik [result] z asynchronicznego zapytania [asyncData] z wierszy 25–65;
- wiersze 28–33: serwer nie wykonuje tych wierszy przeznaczonych dla klienta [nuxt];
- wiersz 36: pobierana jest warstwa [dao] z serwera [nuxt];
- wiersz 37: następuje uwierzytelnienie na serwerze obliczającym podatek przy użyciu testowych danych uwierzytelniających [admin, admin], które są jedynymi akceptowanymi przez ten serwer;
- wiersz 41: operacja uwierzytelniania zakończyła się powodzeniem, o ile odpowiedź ma status 200;
- wiersz 43: do pamięci tymczasowej zapisywana jest właściwość [userAuthenticated];
- wiersze 44–46: stan jest zapisywany w sesji o identyfikatorze [nuxt];
- wiersze 48–51: jeśli uwierzytelnianie nie powiodło się, generowany jest wyjątek z komunikatem o błędzie przesłanym przez serwer obliczający podatek;
- w przeciwnym razie w wierszu 53 zwracany jest wynik pozytywny, który zostanie wyświetlony w wierszu 7;
- wiersze 54–57: w przypadku błędu ustawiane są trzy właściwości strony [result, showErrorLoading, errorLoadingMessage]. Właściwość [result] zostanie wyświetlona w wierszu 7. Trzy właściwości zostaną wysłane do klienta [nuxt];
- wiersze 60–63: nie są wykonywane przez serwer [nuxt];
- gdy [asyncData] zwróci wynik, zostanie on wyświetlony w wierszu 7. Następnie wykonywane są metody [beforeCreate] (wiersze 67–69) i [created] (wiersze 70–72);
- to wszystko;
Uwaga: wykonanie strony [authentification] przez serwer [nuxt] może się nie powieść, na przykład jeśli sesja jSON z serwerem obliczającym podatek nie została zainicjowana. Można to sprawdzić w następujący sposób:
- usuń plik cookie sesji PHP ze swojej przeglądarki (aby zacząć od nowa):

- uruchom aplikację [nuxt], gdy serwer obliczeniowy nie został uruchomiony: pojawi się błąd;
- uruchom serwer obliczający podatek;
- wprowadź adres URL [/authentification] bezpośrednio w pasku adresu przeglądarki:

W takim przypadku jedynym rozwiązaniem jest ponowne załadowanie strony [index].
15.16. Strona [authentification] uruchamiana przez klienta [nuxt]
Przyjrzyjmy się ponownie kodowi strony:
<!-- strona uwierzytelniania -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat-->
<b-alert slot="right" show variant="warning">Authentification auprès du serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'Authentification',
// używane komponenty
components: {
Layout,
Navigation
},
// dane asynchroniczne
async asyncData(context) {
// log
console.log('[authentification asyncData started]')
if (process.client) {
// początek oczekiwania na klienta [nuxt]
context.app.$eventBus().$emit('loading', true)
// brak błędu
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// autoryzacja na serwerze
const dao = context.app.$dao()
const response = await dao.authentifierUtilisateur('admin', 'admin')
// log
console.log('[authentification asyncData response=]', response)
// wynik
const userAuthenticated = response.état === 200
// odnotowuje się, czy użytkownik został uwierzytelniony, czy nie
context.store.commit('replace', { userAuthenticated })
// zapisujemy statek w sesji [nuxt]
const session = context.app.$session()
session.save(context)
// błąd uwierzytelniania?
if (!userAuthenticated) {
// błąd znajduje się w response.réponse
throw new Error(response.réponse)
}
// zwracany jest wynik
return { result: '[succès]' }
} catch (e) {
// zgłaszamy błąd
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[authentification asyncData finished]')
if (process.client) {
// koniec oczekiwania na klienta [nuxt]
context.app.$eventBus().$emit('loading', false)
}
}
},
// cykl życia
beforeCreate() {
console.log('[authentification beforeCreate]')
},
created() {
console.log('[authentification created]')
},
beforeMount() {
console.log('[authentification beforeMount]')
},
mounted() {
console.log('[authentification mounted]')
// tylko dla klientów
if (this.showErrorLoading) {
console.log('[authentification mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
Istnieją dwa przypadki wykonania strony [authentification] przez klienta [nuxt]:
- klient [nuxt] uruchamia się po tym, jak serwer [nuxt] wysłał do przeglądarki klienta [nuxt] stronę [authentification];
- klient [nuxt] uruchamia się, ponieważ użytkownik kliknął link [Authentification] w menu nawigacyjnym:

Przyjrzyjmy się najpierw pierwszemu przypadkowi. W tym przypadku klient [nuxt] nie uruchamia funkcji [asyncData]. Włącza on do właściwości strony elementy [result] oraz ewentualnie [showErrorLoading, errorLoadingMessage], które przesłał mu serwer [nuxt]:
- właściwość [result] jest wyświetlana w wierszu 7;
- właściwości [showErrorLoading, errorLoadingMessage] są wykorzystywane przez metodę [mounted]: w wierszu 79 sprawdzana jest właściwość [showErrorLoading]. Jeśli jest ona prawdziwa, w wierszu 81 wykorzystuje się magistralę zdarzeń klienta [nuxt] w celu zasygnalizowania, że należy wyświetlić komunikat o błędzie;
Mechanizm wyświetlania komunikatu o błędzie został wyjaśniony dla strony [index] w akapicie dotyczącym linku.
Przypadek 2 dotyczy klienta [nuxt] uruchamianego, gdy użytkownik kliknie link [Authentification]. W tym przypadku klient [nuxt] uruchamia się samodzielnie, a nie po serwerze [nuxt]. Następnie wykonywana jest funkcja [asyncData]. Podajemy jedynie szczegóły, które różnią się od wyjaśnień dotyczących strony uruchamianej przez serwer [nuxt]:
- wiersze 28–33: klient [nuxt] żąda wyświetlenia komunikatu o oczekiwaniu oraz usunięcia ewentualnego komunikatu o błędzie, który mógł zostać wcześniej wyświetlony;
- wiersz 36: w tym miejscu pobierana jest teraz warstwa [dao] klienta [nuxt];
- wiersze 60–63: klient [nuxt] żąda zakończenia wyświetlania komunikatu o oczekiwaniu;
- po zakończeniu działania [asyncData] rozpocznie się cykl życia strony. Zostanie wykonana funkcja [mounted] z wierszy 76–83. Jeśli wystąpił błąd, zostanie wyświetlony komunikat o błędzie;
Uwaga: aby wywołać błąd, należy postępować zgodnie z procedurą opisaną dla serwera [nuxt] na końcu akapitu z linkiem, ale zamiast wywoływać stronę [authentification], wpisując jej adres URL w pasku adresu, należy skorzystać z linku [Authentification] w menu nawigacyjnym. Wówczas uruchomi się klient o numerze [nuxt].
15.17. Strona [get-admindata]
Kod strony [get-admindata] jest następujący:
<!-- widok get-admindata -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat -->
<b-alert slot="right" show variant="secondary"> Demande de [adminData] au serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'GetAdmindata',
// używane komponenty
components: {
Layout,
Navigation
},
// dane asynchroniczne
async asyncData(context) {
// log
console.log('[get-admindata asyncData started]')
if (process.client) {
// początek oczekiwania
context.app.$eventBus().$emit('loading', true)
// brak błędu
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// żądanie danych [admindata]
const response = await context.app.$dao().getAdminData()
// log
console.log('[get-admindata asyncData response=]', response)
// wynik
const adminData = response.état === 1000 ? response.réponse : ''
// dane są umieszczane w pamięci tymczasowej
context.store.commit('replace', { adminData })
// zapisujemy bufor w sesji [nuxt]
const session = context.app.$session()
session.save(context)
// czy wystąpił błąd?
if (!adminData) {
// błąd znajduje się w response.réponse
throw new Error(response.réponse)
}
// zwracamy otrzymaną wartość
return { result: adminData }
} catch (e) {
// zgłaszamy błąd
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[get-admindata asyncData finished]')
if (process.client) {
// koniec oczekiwania
context.app.$eventBus().$emit('loading', false)
}
}
},
// cykl życia
beforeCreate() {
console.log('[get-admindata beforeCreate]')
},
created() {
console.log('[get-admindata created]')
},
beforeMount() {
console.log('[get-admindata beforeMount]')
},
mounted() {
console.log('[get-admindata mounted]')
// klient
if (this.showErrorLoading) {
console.log('[get-admindata mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
Strona ta jest bardzo podobna do strony [authentification]. Objaśnienia są analogiczne zarówno w przypadku jej uruchomienia przez serwer [nuxt], jak i przez klienta [nuxt]. Należy jednak zauważyć, że w wierszu 7 nie wyświetla się komunikat o powodzeniu lub niepowodzeniu, jak poprzednio, ale wartość danych otrzymanych z serwera obliczającego podatek (wiersz 52):

Powyższy wynik uzyskuje się zarówno przy użyciu serwera, jak i klienta [nuxt]. Aby wywołać błąd, należy wywołać stronę [get-admindata] za pośrednictwem serwera lub klienta [nuxt] bez uwierzytelnienia:

15.18. Strona [fin-session]
Kod strony jest następujący:
<!-- strona główna -->
<template>
<Layout :left="true" :right="true">
<!-- nawigacja -->
<Navigation slot="left" />
<!-- komunikat-->
<b-alert slot="right" show variant="warning">Fin de la session avec le serveur de calcul de l'impôt : {{ result }} </b-alert>
</Layout>
</template>
<script>
/* eslint-disable no-console */
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
export default {
name: 'FinSession',
// używane komponenty
components: {
Layout,
Navigation
},
// dane asynchroniczne
async asyncData(context) {
// log
console.log('[fin-session asyncData started]')
// przypadek klienta [nuxt]
if (process.client) {
// początek oczekiwania
context.app.$eventBus().$emit('loading', true)
// brak błędu
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// wysyłamy żądanie o nową sesję PHP do serwera obliczającego podatek
const dao = context.app.$dao()
const response = await dao.finSession()
// log
console.log('[fin-session asyncData response=]', response)
// czy wystąpił błąd?
if (response.état !== 400) {
// błąd znajduje się w response.réponse
throw new Error(response.réponse)
}
// serwer wysłał nowy plik cookie sesji PHP
// jest on pobierany zarówno przez serwer, jak i klienta Nuxt
// jeśli ten kod zostanie wykonany przez klienta [nuxt], plik cookie sesji PHP musi zostać umieszczony w sesji Nuxt
//, aby wtyczka [plgDao] serwera [nuxt] mogła go pobrać i zainicjować warstwę [dao] za pomocą
//. Jeśli kod ten jest wykonywany przez serwer [nuxt], plik cookie sesji PHP musi zostać umieszczony w sesji Nuxt
//, aby routing klienta [nuxt] mógł go pobrać i przekazać do przeglądarki
const phpSessionCookie = dao.getPhpSessionCookie()
// w rejestrze odnotowuje się rozpoczęcie sesji jSON i zapisuje się plik cookie sesji PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// zapisuje się zawartość pamięci w sesji [nuxt]
const session = context.app.$session()
session.save(context)
// zwracany jest wynik
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// log
console.log('[fin-session asyncData error=]', e)
// zgłaszamy błąd
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[fin-session asyncData finished]')
if (process.client) {
// koniec oczekiwania
context.app.$eventBus().$emit('loading', false)
}
}
},
// cykl życia
beforeCreate() {
console.log('[fin-session beforeCreate]')
},
created() {
console.log('[fin-session created]')
},
beforeMount() {
console.log('[fin-session beforeMount]')
},
mounted() {
console.log('[fin-session mounted]')
// tylko klient
if (this.showErrorLoading) {
console.log('[fin-session mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
}
</script>
Kod jest bardzo podobny do kodu poprzednich stron, a objaśnienia są takie same. Należy jedynie zwrócić uwagę na jedną kwestię: operacja asynchroniczna w wierszu 38 powoduje, że serwer obliczający podatek wyśle nowy plik cookie sesji o nazwie PHP. Wyjaśnienia dotyczące obsługi tego pliku cookie różnią się w zależności od tego, czy kod ten jest wykonywany przez serwer, czy przez klienta [nuxt].
Zacznijmy od serwera [nuxt]:
- wiersz 37: instancjonowana jest warstwa [dao] serwera [nuxt]. Przypomnijmy kod jej konstruktora:
// konstruktor
constructor(axios, phpSessionCookie) {
// biblioteka axios
this.axios = axios
// wartość pliku cookie sesji
this.phpSessionCookie = phpSessionCookie
// nazwa pliku cookie sesji serwera PHP
this.phpSessionCookieName = 'PHPSESSID'
}
W wierszu 1 widać, że konstruktor potrzebuje aktualnego pliku cookie sesji PHP, czyli ostatniego otrzymanego, niezależnie od tego, czy pochodzi on od serwera, czy od klienta [nuxt];
- w wierszu 52: serwer [nuxt] pobiera plik cookie nowej sesji PHP lub poprzedni plik cookie, jeśli operacja zakończenia sesji nie powiodła się;
- wiersz 54: plik cookie sesji PHP jest umieszczany w pamięci, a następnie zapisywany w sesji [nuxt] w wierszach 56–57;
- po serwerze to klient [nuxt] wykonuje stronę [fin-session] z danymi przesłanymi przez serwer. Wiemy, że nie wykona on funkcji [asyncData];
- ostatecznie, po zakończeniu pracy przez serwer i klienta [nuxt], wiadomo, że plik cookie PHP, niezbędny do komunikacji z serwerem obliczającym podatek, znajduje się w sesji [nuxt];
Fakt, że plik cookie PHP znajduje się w sesji [nuxt], jest wystarczający dla serwera, ponieważ właśnie stamtąd pobierze go jego warstwa [dao]. W wtyczce [server/plgDao], która inicjuje warstwę [dao] serwera, zapisano:
/* eslint-disable no-console */
// tworzymy punkt dostępu do warstwy [Dao]
import Dao from '@/api/server/Dao'
export default (context, inject) => {
// konfiguracja axios
context.$axios.defaults.timeout = context.env.timeout
context.$axios.defaults.baseURL = context.env.baseURL
// pobieramy plik cookie sesji
const store = context.app.$session().value.store
const phpSessionCookie = store ? store.phpSessionCookie : ''
console.log('session=', context.app.$session().value, 'phpSessionCookie=', phpSessionCookie)
// instancjonowanie warstwy [dao]
const dao = new Dao(context.$axios, phpSessionCookie)
// wstrzyknięcie funkcji [$dao] do kontekstu
inject('dao', () => dao)
// logowanie
console.log('[fonction server $dao créée]')
}
- w wierszu 13 warstwa serwera [dao] jest instancjonowana z plikiem cookie sesji PHP pobranym z sesji [nuxt], wiersze 9–10;
W przypadku klienta [nuxt] sytuacja wygląda inaczej. To nie on bowiem wysyła plik cookie, lecz przeglądarka, w której jest uruchomiony. Przeglądarka ta nie zna jednak pliku cookie nowej sesji PHP otrzymanego przez serwer [nuxt]. Jeśli skorzystamy z linków w menu nawigacyjnym [3]:

Serwer obliczający podatek otrzyma z przeglądarki nieaktualny plik cookie sesji o nazwie PHP i odpowie, że z tym plikiem cookie nie jest powiązana żadna sesja o nazwie jSON. Musimy znaleźć sposób, aby przekazać przeglądarce nowy plik cookie sesji o nazwie PHP.
W tym celu można wykorzystać oprogramowanie pośredniczące do routingu:

Skrypt [client/routing] jest oprogramowaniem pośredniczącym do routingu zadeklarowanym w pliku [nuxt.config]:
// router
router: {
// katalog główny aplikacji URL
base: '/nuxt-12/',
// oprogramowanie pośredniczące routingu
middleware: ['routing']
},
Skrypt [middleware/routing] ma następującą postać:
/* eslint-disable no-console */
// importujemy oprogramowanie pośredniczące klienta
import clientRouting from './client/routing'
export default function(context) {
// kto wykonuje ten kod?
console.log('[middleware], process.server', process.server, ', process.client=', process.client)
if (process.client) {
// routing po stronie klienta
clientRouting(context)
}
}
- wiersze 9–12: przekierowywany jest tylko klient za pomocą funkcji zaimportowanej w wierszu 4;
Skrypt [middleware/client/routing] wygląda następująco:
/* eslint-disable no-console */
export default function(context) {
// kto wykonuje ten kod?
console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
// obsługa pliku cookie sesji PHP w przeglądarce
// Plik cookie sesji PHP w przeglądarce musi być identyczny z tym znalezionym w sesji Nuxt
// akcja [fin-session] otrzymuje nowy plik cookie PHP (zarówno serwer, jak i klient Nuxt)
// jeśli to serwer go odbiera, klient musi przekazać go przeglądarce
// w celu własnej komunikacji z serwerem PHP
// mamy tu do czynienia z routingiem po stronie klienta
// pobieramy plik cookie sesji PHP
const phpSessionCookie = context.store.state.phpSessionCookie
if (phpSessionCookie) {
// jeśli istnieje, przypisujemy plik cookie sesji PHP do przeglądarki
document.cookie = phpSessionCookie
}
}
Wróćmy do sytuacji tuż po wykonaniu strony [fin-session] przez serwer [nuxt]:

Jeśli klikniemy jeden z linków w menu [3], kontrolę przejmie klient [nuxt]. Ponieważ nastąpi zmiana strony, uruchomi się skrypt routingu klienta:
- wiersz 13: plik cookie sesji PHP zostaje znaleziony w magazynie aplikacji [nuxt];
- wiersz 14: jeśli nie jest pusty, jest przekazywany do przeglądarki (wiersz 16). Od tego momentu przeglądarka klienta [nuxt] posiada prawidłowy plik cookie sesji PHP;
Skrypt [client/routing] jest uruchamiany przy każdej zmianie strony przez klienta [nuxt]. Kod skryptu działa poprawnie niezależnie od strony docelowej: w większości przypadków po prostu przekazuje przeglądarce plik cookie sesji PHP, który już posiada, z wyjątkiem dwóch sytuacji:
- zaraz po uruchomieniu aplikacji, serwer [nuxt] uruchamia stronę [index] i otrzymuje pierwszy plik cookie sesji PHP, którego nie posiada przeglądarka klienta [nuxt];
- gdy serwer [nuxt] uruchamia stronę [fin-session], tak jak to właśnie wyjaśniono;
Rozważmy teraz przypadek, w którym strona [fin-session] jest wywoływana wyłącznie przez klienta [nuxt], ponieważ kliknięto jej link w menu nawigacyjnym. Teraz to klient [nuxt] uruchamia funkcję [asyncData]:
try {
// wysyłamy żądanie o nową sesję PHP do serwera obliczającego podatek
const dao = context.app.$dao()
const response = await dao.finSession()
// log
console.log('[fin-session asyncData response=]', response)
// czy wystąpił błąd?
if (response.état !== 400) {
// błąd znajduje się w response.réponse
throw new Error(response.réponse)
}
// serwer wysłał nowy plik cookie sesji PHP
// jest on pobierany zarówno przez serwer, jak i klienta Nuxt
// jeśli ten kod zostanie wykonany przez klienta [nuxt], plik cookie sesji PHP musi zostać umieszczony w sesji Nuxt
// aby wtyczka [plgDao] serwera [nuxt] mogła go pobrać i zainicjować warstwę [dao] za pomocą
//. Jeśli kod ten jest wykonywany przez serwer [nuxt], plik cookie sesji PHP musi zostać umieszczony w sesji Nuxt
//, aby routing klienta [nuxt] mógł go pobrać i przekazać do przeglądarki
const phpSessionCookie = dao.getPhpSessionCookie()
// odnotowujemy w magazynie, że sesja jSON została uruchomiona i zapisujemy plik cookie sesji PHP
context.store.commit('replace', { jsonSessionStarted: true, phpSessionCookie, userAuthenticated: false, adminData: '' })
// zapisuje się zawartość pamięci w sesji [nuxt]
const session = context.app.$session()
session.save(context)
// zwracany jest wynik
return { result: "[succès]. La session jSON reste initialisée mais vous n'êtes plus authentifié(e)." }
} catch (e) {
// log
console.log('[fin-session asyncData error=]', e)
// zgłaszamy błąd
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// log
console.log('[fin-session asyncData finished]')
if (process.client) {
// koniec oczekiwania
context.app.$eventBus().$emit('loading', false)
}
}
- wiersz 3: tutaj uzyskiwana jest warstwa [dao] klienta [nuxt];
- wiersz 18: plik cookie sesji PHP pobrany przez warstwę [dao] klienta [nuxt] jest zapisywany w pamięci, umieszczany w magazynie (wiersz 20), a następnie zapisany w sesji [nuxt] (wiersze 22–23);
- od tego momentu wszystko przebiega prawidłowo, ponieważ wiadomo, że warstwa [dao] serwera [nuxt] pobiera plik cookie sesji PHP z sesji [nuxt];
15.19. Exécution
Aby uruchomić ten przykład, przed wykonaniem należy usunąć plik cookie sesji [nuxt] oraz plik cookie PHP z przeglądarki, w której uruchomiony jest klient [nuxt], aby rozpocząć od czystej sytuacji. Poniżej znajduje się przykład z przeglądarką Chrome:

15.20. Conclusion
Ten przykład był szczególnie złożony. Łączył on wiedzę zdobytą w poprzednich przykładach: trwałość pamięci podręcznej w sesji [nuxt], wtyczki do wstrzykiwania funkcji, oprogramowanie pośredniczące do routingu oraz obsługę błędów operacji asynchronicznych. Złożoność została zwiększona przez fakt, że chcieliśmy, aby użytkownik mógł zarówno korzystać z linków w menu nawigacyjnym, jak i ręcznie wpisywać adresy URL, bez powodowania awarii aplikacji. W tym celu musieliśmy przeanalizować, jak zachowuje się każda strona w zależności od tego, czy jest wykonywana przez klienta, czy przez serwer [nuxt].
Ta spójność zachowania klienta i serwera [nuxt] nie jest niezbędna. Można rozważyć częsty przypadek, w którym:
- pierwsza strona jest dostarczana przez serwer [nuxt];
- wszystkie kolejne strony są dostarczane przez klienta [nuxt], który działa wówczas w trybie [SPA];
Niemniej jednak nawet w tym przypadku należy sprawdzić, jaki będzie wynik wygenerowania wszystkich stron przez serwer [nuxt], ponieważ właśnie to otrzymają wyszukiwarki, które je zażądają.