38. Ćwiczenie praktyczne: wersja 18
38.1. Implementacja

Folder [impots/http-servers/13] powstaje początkowo poprzez skopiowanie folderu [impots/http-servers/12], a następnie jest częściowo modyfikowany.
Zaczynamy od dodania nowego parametru w pliku [configs/parameters]:
…
# token CSRF
"with_csrftoken": False,
# obsługiwane bazy danych: MySQL (MySQL), PostgreSQL (PostgreSQL)
"databases": ["mysql", "pgres"],
# prefiks URL aplikacji
# należy ustawić pusty ciąg znaków, jeśli nie chcemy stosować prefiksu, w przeciwnym razie należy wpisać /prefiks
"prefix_url": "/do",
# adres główny serwera Apache – należy ustawić pusty ciąg znaków, aby uruchomić program poza serwerem Apache
"application_root": "/impots"
…
W wierszu 10 parametr [application_root] będzie reprezentował alias WSGI serwera wirtualnego Apache.
Dzięki temu parametrowi możemy skorygować instrukcję [responses/HtmlResponse], która spowodowała błąd:

…
# teraz należy wygenerować przekierowanie URL, nie zapominając o tokenie CSRF, jeśli jest wymagany
if config['parameters']['with_csrftoken']:
csrf_token = f"/{generate_csrf()}"
else:
csrf_token = ""
# odpowiedź przekierowania
return redirect(f"{config['parameters']['application_root']}{config['parameters']['prefix_url']}{ads['to']}{csrf_token}")
, status.HTTP_302_FOUND
- wiersz 9: dodaliśmy katalog główny aplikacji na początku adresu docelowego przekierowania URL;
Musimy również poprawić wszystkie fragmenty, tak aby zawarte w nich URL zaczynały się od katalogu głównego aplikacji (lub aliasu WSGI):

Fragment [v-authentification]
<!-- formularz HTML – wartości wysyła się za pomocą akcji [authentifier-utilisateur] -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}"
>
<!-- tytuł -->
<div class="alert alert-primary" role="alert">
<h4>Veuillez vous authentifier</h4>
</div>
…
</form>
Fragment [v-calcul-impot]
<!-- formularz HTML wysłany -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
<!-- komunikat w 12 kolumnach na niebieskim tle -->
…
</form>
Fragment [v-liste-simulations]
…
{% if modèle.simulations is defined and modèle.simulations|length!=0 %}
…
<!-- tabela symulacji -->
<table class="table table-sm table-hover table-striped">
…
<tr>
<th scope="row">{{simulation.id}}</th>
<td>{{simulation.marié}}</td>
<td>{{simulation.enfants}}</td>
<td>{{simulation.salaire}}</td>
<td>{{simulation.impôt}}</td>
<td>{{simulation.surcôte}}</td>
<td>{{simulation.décôte}}</td>
<td>{{simulation.réduction}}</td>
<td>{{simulation.taux}}</td>
<td><a href="{{modèle.application_root}}{{modèle.prefix_url}}/supprimer-simulation/{{simulation.id}}{{modèle.csrf_token}}">Supprimer</a></td>
</tr>
{% endfor %}
</tr>
</tbody>
</table>
{% endif %}
Fragment [v-menu]
<!-- menu Bootstrap -->
<nav class="nav flex-column">
<!-- wyświetlanie listy linków HTML -->
{% for optionMenu in modèle.optionsMenu %}
<a class="nav-link" href="{{modèle.application_root}}{{modèle.prefix_url}}{{optionMenu.url}}{{modèle.csrf_token}}">{{optionMenu.text}}</a>
{% endfor %}
</nav>
Wszystkie powyższe fragmenty wykorzystują szablon [modèle.application_root]. Na razie klucz [application_root] nie istnieje w szablonach generowanych przez klasy szablonów.

Klasa [AbstractBaseModelForView], która jest klasą nadrzędną wszystkich klas generujących szablon, przyjmuje następujący kształt:
from abc import abstractmethod
from flask import Request
from flask_wtf.csrf import generate_csrf
from werkzeug.local import LocalProxy
from InterfaceModelForView import InterfaceModelForView
class AbstractBaseModelForView(InterfaceModelForView):
@abstractmethod
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
pass
def update_model(self, modèle: dict, config: dict):
# obliczanie tokenu CSRF
if config['parameters']['with_csrftoken']:
csrf_token = f"/{generate_csrf()}"
else:
csrf_token = ""
# aktualizacja szablonu przekazanego jako parametr
modèle.update({
# token CSRF
'csrf_token': csrf_token,
# prefix_url
'prefix_url': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJwcmVmaXhfdXJsIl0ZQX,
# application_root
'application_root': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJhcHBsaWNhdGlvbl9yb290Il0ZQX,
})
- wiersz 15: metoda [update_model] służy do umieszczania w szablonie widoków:
- wiersz 24: token CSRF;
- wiersz 26: prefiks URL;
- wiersz 28: katalog główny aplikacji lub alias WSGI;
Cztery klasy potomne wywołują klasę nadrzędną za pomocą następującego kodu:
…
# możliwe działania z poziomu widoku
modèle['actions_possibles'] = ["afficher-vue-authentification", "authentifier-utilisateur"]
# dokończenie modelu przez klasę nadrzędną
super().update_model(modèle, config)
# zwracamy szablon
return modèle
- wiersz 6: każda klasa potomna wywołuje swoją klasę nadrzędną w celu aktualizacji utworzonego przez siebie modelu;
Wersja 18 jest gotowa. Przenosimy dwa wirtualne serwery Apache z wersji 17 i modyfikujemy je:

Oba pliki [flask-impots-withXX.conf] zostały zmienione tylko w jednym miejscu:
# folder skryptu .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/13/apache"
# nazwa strony internetowej skonfigurowana przez ten plik
# tutaj będzie to flask-impots-withmysql
# adresy będą miały postać http(s)://flask-impots-withmysql/ścieżka
define SITE "flask-impots-withmysql"
# w pliku hosts w katalogu c:/windows/system32/drivers/etc/hosts należy wpisać adres 127.0.0.1 dla strony SITE
# wpisać tutaj ścieżki do bibliotek Python, które mają być używane – oddzielić je przecinkami
# tutaj należy wpisać biblioteki wirtualnego środowiska Python
WSGIPythonPath "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"
# Katalog główny Pythona – wymagane tylko wtedy, gdy zainstalowanych jest kilka wersji Pythona
# WSGIPythonHome „C:/Program Files/Python38”
# URL HTTP
<VirtualHost *:80>
# z aliasem /, natomiast URL będą miały postać /{prefixe_url}/action/...
# z aliasem /impots, URL będą miały postać /impots/{prefixe_url}/action/...
#, gdzie [prefixe_url] jest zdefiniowane w parameters.py
WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
…
</VirtualHost>
# URL zabezpieczone za pomocą HTTPS
<VirtualHost *:443>
# z aliasem /, a URL będą miały postać /{prefixe_url}/action/...
# z aliasem /impots, a adresy URL będą miały postać /impots/{prefixe_url}/action/...
#, gdzie [prefixe_url] jest zdefiniowane w parameters.py
WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
..
</VirtualHost>
- w wierszu 12 używany jest teraz folder [impots/http-servers/13/apache].
Jesteśmy gotowi do testów wersji 18 przeniesionej na Apache. Konfiguracja jest następująca:
- alias WSGI to /impots w obu plikach konfiguracyjnych serwerów wirtualnych;
- w pliku konfiguracyjnym [configs/parameters] parametry są następujące:
# token CSRF
"with_csrftoken": False,
# prefiks URL aplikacji
# ustaw pusty ciąg znaków, jeśli nie chcesz prefiksu, w przeciwnym razie wpisz /prefiks
"prefix_url": "/do",
# adres główny serwera Apache — należy ustawić pusty ciąg znaków, aby uruchomić aplikację poza serwerem Apache
"application_root": "/impots"
Uruchamiamy serwer Apache oraz oba serwery SGBD. Wysyłamy żądanie do serwerów URL i [https://flask-impots-withmysql/impots/do]. Odpowiedź serwera jest następująca:

Pojawia się ekran uwierzytelniania, którego nie udało się wyświetlić w poprzedniej wersji. Pozostała część aplikacji działa normalnie.
Teraz testujemy drugi serwer wirtualny. Wysyłamy żądanie o kodach URL i [https://flask-impots-withpgres/impots/do]. Odpowiedź serwera jest następująca:

Pojęcia aliasu WSGI i prefiksu URL pełnią tę samą rolę. Jedno z tych dwóch pojęć jest zbędne. Aby więc dodać przedrostek do adresów URL serwera Apache za pomocą ciągu [/impots/do], można to zrobić na trzy sposoby:
1 – [WGSIAlias /impots] i [prefix_url=’/do’];
2 – [WGSIAlias /] i [prefix_url=’/impots/do’];
3 – [WGSIAlias /impots/do] i [prefix_url=’’];
38.2. Testy konsoli
Ponownie wykorzystujemy testy konsoli klienta [http-clients/09]:

- konfiguracje [1] i [3] wymagają modyfikacji serwera URL;
- należy wprowadzić zmianę w warstwie [dao], aby obsługiwała protokół HTTPS serwera Apache;
W plikach [config] wartość URL serwera przyjmuje następującą postać:
"server": {
# „urlServer”: „http://127.0.0.1:5000”,
# „urlServer”: „http://127.0.0.1:5000/do”,
"urlServer": "https://flask-impots-withmysql/impots/do",
"user": {
"login": "admin",
"password": "admin"
},
"url_services": {
…
}
},
# tryb debugowania
"debug": True,
# csrf_token
"with_csrftoken": False,
- wiersz 4: nowy protokół serwera URL. Po raz pierwszy w tym dokumencie klient korzysta z protokołu HTTPS;
Klasa [ImpôtsDaoWihHttpSession] warstwy [dao] zmienia się w następujący sposób:
…
# etap żądania / odpowiedzi
def get_response(self, method: str, url_service: str, data_value: dict = None, json_value=None):
# [method]: metoda HTTP, GET lub POST
# [url_service]: URL usługi
# [data]: parametry POST w formacie x-www-form-urlencoded
# [json]: parametry POST w formacie JSON
# [cookies]: pliki cookie, które należy dołączyć do żądania
# należy posiadać sesję XML lub JSON, w przeciwnym razie nie będzie można obsłużyć odpowiedzi
if self.__session_type not in ['json', 'xml']:
raise ImpôtsError(73, "il n'y a pas de session valide en cours")
# do żądania serwisowego URL dodaje się token CSRF
if self.__csrf_token:
url_service = f"{url_service}/{self.__csrf_token}"
# wykonanie żądania
response = requests.request(method,
url_service,
data=data_value,
json=json_value,
cookies=self.__cookies,
allow_redirects=True,
# dla protokołu https
verify=False)
# tryb debugowania?
if self.__debug:
# użytkownik
if not self.__logger:
self.__logger = self.__config['logger']
# logowanie
self.__logger.write(f"{response.text}\n")
…
# zwracamy wynik
return résultat['réponse']
- w wierszu 26 dodano parametr [verify=False] ze względu na protokół HTTPS używany przez serwer Apache. Moduł [requests] (wiersz 19) obsługuje natywnie protokół HTTPS. Domyślnie moduł ten sprawdza poprawność certyfikatu bezpieczeństwa przesłanego przez serwer HTTPS i zgłasza wyjątek, jeśli otrzymany certyfikat jest nieprawidłowy. Tak właśnie jest w tym przypadku, gdzie serwer Apache w Laragon wysyła certyfikat z podpisem własnym. Aby uniknąć wyjątku, używamy parametru [verify=False], aby poinformować moduł [requests], że nie ma zgłaszać wyjątku. [requests] wyświetla wówczas jedynie ostrzeżenie (warning) na konsoli.
Po wprowadzeniu tych zmian wszystkie testy konsolowe powinny działać poprawnie.