Skip to content

38. Ćwiczenie praktyczne: wersja 18

38.1. Implementacja

Image

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:

Image



# 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):

Image

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.

Image

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:

Image

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:

Image

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:

Image

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]:

Image

  • 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.