Skip to content

38. Практичне завдання: версія 18

38.1. Реалізація

Image

Файл [impots/http-servers/13] спочатку створюється шляхом копіювання файлу [impots/http-servers/12], а потім частково модифікується.

Почнемо з додавання нового параметра у файл [configs/parameters]:


…        
         # токен CSRF
        "with_csrftoken"False,
        # керовані бази даних MySQL (mysql), PostgreSQL (pgres)
        "databases"["mysql""pgres"],
        # префікс URL додатка
        # вказати порожній рядок, якщо префікс не потрібен, або /префікс в іншому випадку
        "prefix_url""/do",
        # кореневий URL-адресу сервера Apache — вкажіть порожній рядок для запуску поза Apache
        "application_root""/impots"

У рядку 10 параметр [application_root] буде представляти псевдонім WSGI віртуального сервера Apache.

За допомогою цього параметра ми можемо виправити інструкцію [responses/HtmlResponse], яка спричинила помилку:

Image



# тепер потрібно згенерувати URL для перенаправлення, не забуваючи про токен CSRF, якщо його вимагають
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

        # відповідь на перенаправлення
        return redirect(f"{config['parameters']['application_root']}{config['parameters']['prefix_url']}{ads['to']}{csrf_token}")
, status.HTTP_302_FOUND
  • рядок 9: ми додали кореневий каталог додатка на початок адреси перенаправлення URL;

Також потрібно виправити всі фрагменти, щоб URL, які вони містять, починалися з кореневого каталогу додатка (або псевдоніма WSGI):

Image

Фрагмент [v-authentification]


<!-- форма HTML — відправляємо її значення за допомогою дії [authentifier-utilisateur] -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}"
>

    <!-- заголовок -->
    <div class="alert alert-primary" role="alert">
        <h4>Veuillez vous authentifier</h4>
    </div>


</form>

Фрагмент [v-calcul-impot]


<!-- форма HTML, надіслана -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
    <!-- повідомлення у 12 стовпчиках на синьому тлі -->
    

</form>

Фрагмент [v-liste-simulations]




{% if modèle.simulations is defined and modèle.simulations|length!=0 %}


<!-- таблиця моделювань -->
<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 %}

Фрагмент [v-menu]


<!-- меню Bootstrap -->
<nav class="nav flex-column">
    <!-- відображення списку посилань 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>

Усі наведені вище фрагменти використовують шаблон [modèle.application_root]. Наразі ключ [application_root] не існує серед шаблонів, що генеруються класами шаблонів.

Image

Клас [AbstractBaseModelForView], який є батьківським класом для всіх класів, що генерують шаблон, набуває такого вигляду:


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):
        # розрахунок токена CSRF
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""
        # оновлення шаблону, переданого як параметр
        modèle.update({
            # токен CSRF
            'csrf_token': csrf_token,
            # prefix_url
            'prefix_url': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJwcmVmaXhfdXJsIl0ZQX,
            # application_root
            'application_root': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJhcHBsaWNhdGlvbl9yb290Il0ZQX,
        })
  • рядок 15: метод [update_model] призначений для вставлення в шаблон переглядів:
    • рядок 24: токен CSRF;
    • рядок 26: префікс URL;
    • рядок 28: корінь додатка або псевдонім WSGI;

Чотири дочірні класи викликають батьківський клас за допомогою такого коду:



        # можливі дії з цього перегляду
        modèle['actions_possibles'] = ["afficher-vue-authentification""authentifier-utilisateur"]

        # доопрацювання моделі батьківським класом
        super().update_model(modèle, config)

        # виведення моделі
        return modèle
  • рядок 6: кожен дочірній клас викликає свій батьківський клас для оновлення створеної ним моделі;

Версія 18 готова. Ми беремо два віртуальні сервери Apache з версії 17 і модифікуємо їх:

Image

Обидва файли [flask-impots-withXX.conf] змінено лише в одному місці:


# папка скрипту .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/13/apache"

# назва веб-сайту, налаштована цим файлом
# тут він називатиметься flask-impots-withmysql
# адреси URL матимуть вигляд http(s)://flask-impots-withmysql/шлях
define SITE "flask-impots-withmysql"

# введіть адресу IP 127.0.0.1 для сайту SITE у файл c:/windows/system32/drivers/etc/hosts

# вказати тут шляхи до бібліотек Python, які потрібно використовувати — розділити їх комами
# тут вказати бібліотеки віртуального середовища Python
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"

# Python Home — потрібно лише у разі, якщо встановлено кілька версій Python
# WSGIPythonHome "C:/Program Files/Python38"

# URL HTTP
<VirtualHost *:80>
    # з псевдонімом /, а URL матимуть вигляд /{prefixe_url}/action/...
    # з псевдонімом /impots, URL матимуть вигляд /impots/{prefixe_url}/action/...
    #, де [prefixe_url] визначено в parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …
</VirtualHost>

# URL, захищені за допомогою HTTPS
<VirtualHost *:443>
    # з псевдонімом /, URL матимуть вигляд /{prefixe_url}/action/...
    # з псевдонімом /impots, URL матимуть вигляд /impots/{prefixe_url}/action/...
    #, де [prefixe_url] визначено в parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    ..

</VirtualHost>
  • у рядку 12 тепер використовується папка [impots/http-servers/13/apache].

Ми готові до тестування версії 18, перенесеної на Apache. Конфігурація така:

  • псевдонім WSGI — це /impots в обох файлах конфігурації віртуальних серверів;
  • у файлі налаштувань [configs/parameters] параметри такі:

        # токен CSRF
        "with_csrftoken"False,
        # префікс URL додатка
        # встановити порожній рядок, якщо префікс не потрібен, або /префікс в іншому випадку
        "prefix_url""/do",
        # кореневий URL-адресу сервера Apache — вкажіть порожній рядок для запуску поза Apache
        "application_root""/impots"

Запускаємо сервер Apache, а також обидва SGBD. Надіслали запит на URL та [https://flask-impots-withmysql/impots/do]. Відповідь сервера така:

Image

Ми отримуємо вікно автентифікації, яке не вдалося відкрити в попередній версії. Решта додатка працює нормально.

Тепер перевіряємо інший віртуальний сервер. Надсилаємо запит на URL та [https://flask-impots-withpgres/impots/do]. Відповідь сервера така:

Image

Поняття «псевдонім» WSGI та «префікс» URL виконують одну й ту саму роль. Одне з цих двох понять є зайвим. Отже, щоб додати префікс [/impots/do] до URL сервера Apache, можна скористатися трьома способами:

1 — [WGSIAlias /impots] та [prefix_url=’/do’];

2 — [WGSIAlias /] та [prefix_url=’/impots/do’];

3 — [WGSIAlias /impots/do] та [prefix_url=’’];

38.2. Тести консолі

Знову використовуються консольні тести клієнта [http-clients/09]:

Image

  • URL сервера потрібно змінити у конфігураціях [1] та [3];
  • необхідно внести зміну до шару [dao], щоб він підтримував протокол HTTPS сервера Apache;

У файлах [config] значення URL сервера стає таким:


        "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": {
                
            }
        },
        # режим налагодження
        "debug"True,
        # csrf_token
        "with_csrftoken"False,
  • рядок 4: новий URL сервера. Вперше в цьому документі клієнт використовує протокол HTTPS;

Клас [ImpôtsDaoWihHttpSession] шару [dao] змінюється наступним чином:


…    
     # етап запиту/відповіді
    def get_response(self, method: str, url_service: str, data_value: dict = None, json_value=None):
        # [method]: метод HTTP, GET або POST
        # [url_service]: сервісний URL
        # [data]: параметри POST у форматі x-www-form-urlencoded
        # [json]: параметри POST у форматі json
        # [cookies]: файли cookie, які слід включити в запит

        # потрібно мати сесію XML або JSON, інакше не вдасться обробити відповідь
        if self.__session_type not in ['json''xml']:
            raise ImpôtsError(73"il n'y a pas de session valide en cours")

        # до сервісного URL додається токен CSRF
        if self.__csrf_token:
            url_service = f"{url_service}/{self.__csrf_token}"

        # виконання запиту
        response = requests.request(method,
                                    url_service,
                                    data=data_value,
                                    json=json_value,
                                    cookies=self.__cookies,
                                    allow_redirects=True,
                                    # для протоколу https
                                    verify=False)

        # режим налагодження?
        if self.__debug:
            # користувач
            if not self.__logger:
                self.__logger = self.__config['logger']
            # вхід
            self.__logger.write(f"{response.text}\n")

        

        # виводиться результат
        return résultat['réponse']
  • у рядку 26 додається параметр [verify=False] через протокол HTTPS, який використовує сервер Apache. Модуль [requests] (рядок 19) підтримує протокол HTTPS на рівні ядра. За замовчуванням він перевіряє дійсність сертифіката безпеки, який надсилає йому сервер HTTPS, і генерує виняток, якщо отриманий сертифікат є недійсним. Саме це відбувається в даному випадку, коли сервер Apache Laragon надсилає самопідписаний сертифікат. Щоб уникнути винятку, використовується параметр [verify=False], який вказує модулю [requests] не генерувати виняток. У такому разі [requests] просто виводить попередження (warning) на консоль.

Після внесення цих змін усі консольні тести мають працювати.