Skip to content

37. Anwendungsübung: Version 17

Image

Diese neue Version bringt folgende Neuerungen mit sich:

  • Sie wird auf einen Apache-/Windows-Server portiert;
  • Um diese Portierung durchzuführen, enthält Version 17 alle erforderlichen Abhängigkeiten in ihrem Ordner [impots / http-servers/ 12]. Zur Erinnerung: Frühere Versionen holten ihre Abhängigkeiten aus verschiedenen Ordnern des gesamten Projekts [python-flask-2020];

37.1. Verschiebung der Anwendungsabhängigkeiten

Image

Zur Erinnerung: Die Verwaltung der Anwendungsabhängigkeiten erfolgt im Skript [syspath]. In der vorherigen Version lautete dieses Skript wie folgt:


def configure(config: dict) -> dict:
    import os

    # Ordner dieser Datei
    script_dir = os.path.dirname(os.path.abspath(__file__))

    # Stammverzeichnis
    root_dir = "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020"

    # Abhängigkeiten
    absolute_dependencies = [
        # Projektordner
        # BaseEntity, MyException
        f"{root_dir}/classes/02/entities",
        # InterfaceImpôtsDao, InterfaceImpôtsMétier, InterfaceImpôtsUi
        f"{root_dir}/impots/v04/interfaces",
        # AbstractImpôtsdao, ImpôtsConsole, ImpôtsMétier
        f"{root_dir}/impots/v04/services",
        # ImpotsDaoWithAdminDataInDatabase
        f"{root_dir}/impots/v05/services",
        # AdminData, ImpôtsError, TaxPayer
        f"{root_dir}/impots/v04/entities",
        # Konstanten, Tranchen
        f"{root_dir}/impots/v05/entities",
        # Logger, SendAdminMail
        f"{root_dir}/impots/http-servers/02/utilities",
        # Ordner des Hauptskripts
        script_dir,
        # Konfigurationen [database, layers, parameters, controllers, views]
        f"{script_dir}/../configs",
        # Controller
        f"{script_dir}/../controllers",
        # Antworten HTTP
        f"{script_dir}/../responses",
        # Ansichtsvorlagen
        f"{script_dir}/../models_for_views",
    ]

    # Der Syspath wird festgelegt
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # Konfiguration wird bereitgestellt
    return {
        "root_dir": root_dir,
        "script_dir": script_dir
    }

Es müssen alle Abhängigkeiten verschoben werden, deren absoluter Name von der Variablen [root_dir] in Zeile 8 abhängt, d. h. die Zeilen 13–26.

Das Skript [syspath] der neuen Version lautet wie folgt:


def configure(config: dict) -> dict:
    import os

    # Ordner dieser Datei
    script_dir = os.path.dirname(os.path.abspath(__file__))

    # Abhängigkeiten
    absolute_dependencies = [
        # Anwendungskomponenten
        f"{script_dir}/../entities",
        # Schicht [dao]
        f"{script_dir}/../layers/dao",
        # Schicht [métier]
        f"{script_dir}/../layers/métier",
        # Dienstprogramme
        f"{script_dir}/../utilities",
        # Ordner des Hauptskripts
        script_dir,
        # Konfigurationen [database, layers, parameters, controllers, views]
        f"{script_dir}/../configs",
        # Controller
        f"{script_dir}/../controllers",
        # Antworten HTTP
        f"{script_dir}/../responses",
        # Ansichtsvorlagen
        f"{script_dir}/../models_for_views",
    ]

    # Der Syspath wird festgelegt
    import sys
    # Die absoluten Abhängigkeiten des Projekts werden hinzugefügt
    for directory in absolute_dependencies:
        # Prüfung, ob der Ordner vorhanden ist
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            # Der Entwickler wird benachrichtigt
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            # Der Ordner wird am Anfang des Syspath eingefügt
            sys.path.insert(0, directory)

    # die Konfiguration wird wiederhergestellt
    return {
        "script_dir": script_dir,
    }
  • Zeilen 8–27: Alle Abhängigkeiten beziehen sich nun auf die Variable [script_dir] in Zeile 5;
  • Zeilen 42–45: Die Variable [root_dir] ist aus der Syspath-Konfiguration verschwunden;
  • Zeile 10: Die Anwendungskomponenten befinden sich im Ordner „[entities] [1]“;
  • Zeile 12: Die Ebene [dao] befindet sich im Ordner [layers/dao] [2];
  • Zeile 14: Die Ebene [métier] befindet sich im Ordner [layers/métier] [2];
  • Zeile 16: Die Dienstprogramme [Logger, SendMail] befinden sich im Ordner [utilities] [3];
  • Zeilen 29–40: Der Python-Pfad der Anwendung wird berechnet, ohne das Modul [myutils] zu importieren;

Image

37.2. Tests

Zu diesem Zeitpunkt sollte Version 17 funktionieren. Überprüfen Sie dies.

37.3. Portierung einer Python-/Flask-Anwendung auf einen Apache-/Windows-Server

37.3.1. Quellen

Um eine Flask-Anwendung auf Apache/Windows zu portieren, musste ich im Internet recherchieren. Hier ist der Link, der mir den Einstieg erleichtert hat: [https://medium.com/@madumalt/flask-app-deployment-in-windows-apache-server-mod-wsgi-82e1cfeeb2ed];

Ich habe die Informationen aus diesem Link verwendet, mit Ausnahme der Konfiguration des Apache-Servers. Hierfür habe ich ein Beispiel für eine Apache-Serverkonfiguration von Laragon verwendet.

37.3.2. Installation des Python-Moduls mod_wsgi

Die von uns entwickelte Python-/Flask-Anwendung nutzte den mit Flask mitgelieferten Web-Server (Web Server Gateway Interface) WSGI [werkzeug]. Dieser Server wird |hier| beschrieben. Der Link [https://www.fullstackpython.com/wsgi-servers.html] beschreibt die Funktionsweise der WSGI-Server. Es gibt verschiedene |WSGI-Server|. Einer davon ist der Apache-Server, der im WSGI-Modus läuft. Diese Lösung haben wir hier gewählt, da Laragon, das wir installiert haben, einen Apache-Server enthält.

Damit der Apache-Server eine Python-Anwendung hosten kann, müssen wir das Python-Modul [mod_wsgi] installieren. Die Installation dieses Moduls ist etwas knifflig, da dabei eine C++-Kompilierung stattfindet. Für eine erfolgreiche Installation ist ein Microsoft C++-Compiler erforderlich. Eine einfache Lösung besteht darin, die aktuelle Version von Visual Studio Community [https://visualstudio.microsoft.com/fr/vs/community/] zu installieren.

Wenn man Visual Studio ausschließlich für [mod_wsgi] benötigt, kann man die Installation auf die C++-Umgebung beschränken:

Image

Sobald der C++-Compiler installiert ist, erfolgt die Installation des Moduls [mod_wsgi] in einem Terminal PyCharm:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\http-servers\11>SET MOD_WSGI_APACHE_ROOTDIR=C:\MyPrograms\laragon\bin\apache\httpd-2.4.35-win64-VC15

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\http-servers\11>pip install mod_wsgi
Collecting mod_wsgi
  Using cached mod_wsgi-4.7.1.tar.gz (498 kB)
Using legacy setup.py install for mod-wsgi, since package 'wheel' is not installed.
Installing collected packages: mod-wsgi
    Running setup.py install for mod-wsgi ... done
Successfully installed mod-wsgi-4.7.1
  • Zeile 1: Hier wird der Wert der Umgebungsvariable [MOD_WSGI_APACHE_ROOTDIR] festgelegt. Dieser Wert gibt den Speicherort des Apache-Servers im Dateisystem an. In diesem Fall lautet der Speicherort [<laragon>\bin\apache\httpd-2.4.35-win64-VC15], wobei <laragon> der Installationsordner von Laragon ist. Sie können diesen Pfad auf verschiedene Weise ermitteln. Hier ist ein Beispiel, das anhand einer der Optionen von Laragon ermittelt wurde:

Image

In [1-3] ist die Datei [httpd.conf] die Hauptkonfigurationsdatei des Apache-Servers. Die betreffende Datei wird dann in einem Texteditor (im folgenden Beispiel Notepad++) geöffnet:

Image

In [2] ist der Apache-Installationsordner der Teil, der vor der Zeichenfolge [conf\httpd.conf] steht.

Kommen wir nun zur Installation des Moduls [mod_wsgi] zurück:

  • Zeilen 3–9: Installation des Moduls [mod_wsgi];

37.3.3. Konfiguration des Apache-Servers von Laragon

Wir werden den Apache-Server von Laragon konfigurieren. Wir beginnen mit seiner Hauptkonfigurationsdatei [httpd.conf]:

Image

Wir gehen zum Ende der Datei [httpd.conf]:



IncludeOptional "C:/MyPrograms/laragon/etc/apache2/alias/*.conf"
IncludeOptional "C:/MyPrograms/laragon/etc/apache2/sites-enabled/*.conf"
Include "C:/MyPrograms/laragon/etc/apache2/httpd-ssl.conf"
Include "C:/MyPrograms/laragon/etc/apache2/mod_php.conf"

# Python mod_wsgi
LoadModule wsgi_module "c:/data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages/mod_wsgi/server/mod_wsgi.cp38-win_amd64.pyd"
  • Zeile 8 wurde am Ende der Datei [httpd.conf] hinzugefügt. Sie teilt dem Apache-Server mit, wo sich ein Element des Moduls [mod_wsgi] befindet, das wir gerade installiert haben;

Eine einfache Möglichkeit, den Pfad von Zeile 8 zu ermitteln, besteht darin, den folgenden Befehl in einem Terminal auszuführen: PyCharm:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\http-servers\11>mod_wsgi-express module-config
LoadModule wsgi_module "c:/data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages/mod_wsgi/server/mod_wsgi.cp38-win_amd64.pyd"
WSGIPythonHome "c:/data/st-2020/dev/python/cours-2020/python3-flask-2020/venv"

In einigen Dokumentationen wird angegeben, dass die Zeilen 2 und 3 am Ende der Datei [httpd.conf] hinzugefügt werden müssen. In meinem Fall führte die oben genannte Zeile 3 zu einem Fehler (Modul [encodings] fehlt). Daher wurde sie nicht in die Datei [httpd.conf] aufgenommen. Nur Zeile 2 wurde dort eingefügt. Die Bedeutung der verschiedenen Parameter des Moduls [mod_wsgi], die in den Apache-Konfigurationsdateien verwendet werden können, wird |hier| beschrieben.

Anschließend aktivieren wir das Protokoll HTTPS des Apache-Servers:

Image

  • In [1-4] aktivieren wir das Protokoll HTTPS des Apache-Servers;

Nun können wir URL und [https://serveur/chemin] verwenden;

Um Apache so zu konfigurieren, dass er eine Flask-Anwendung bereitstellt, werden im |oben| genannten Link virtuelle Server verwendet. Laragon bietet ebenfalls die Verwaltung virtueller Server an:

Image

  • In [1-3] weisen wir Laragon an, automatisch virtuelle Hosts zu erstellen;

Der nächste Schritt besteht darin, ein Webprojekt mit Laragon zu erstellen:

 
  • In [1-3] wird ein leeres Projekt PHP erstellt;
  • in [4-8] hat Laragon eine virtuelle Website namens [auto.projet-test.test] erstellt, die durch die Datei [auto.projet-test.test.conf] [8] im Ordner [sites-enabled] [7] konfiguriert wurde. Dieser Ordner befindet sich unter der Adresse [<laragon>\etc\apache2\sites-enabled], wobei [laragon] der Installationsordner von Laragon ist;

Auch wenn dies nicht zum aktuellen Thema gehört, möchten Sie vielleicht aus Neugierde einen Blick auf die Website [projet-test] werfen, die wir gerade erstellt haben:

Image

  • In [1-5] wurde ein leeres Projekt erstellt. Es handelt sich um ein Projekt namens PHP, das sich im Ordner [<laragon>/www] befindet, wobei [laragon] der Installationsordner von Laragon ist;

Sehen wir uns nun die von Laragon im Ordner „[<laragon>\etc\apache2\sites-enabled]“ erstellte Datei „[auto.projet-test.test.conf]“ an:


define ROOT "C:/MyPrograms/laragon/www/projet-test/"
define SITE "projet-test.test"

<VirtualHost *:80> 
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

<VirtualHost *:443>
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>

    SSLEngine on
    SSLCertificateFile      C:/MyPrograms/laragon/etc/ssl/laragon.crt
    SSLCertificateKeyFile   C:/MyPrograms/laragon/etc/ssl/laragon.key
 
</VirtualHost>
  • Zeile 1: das Stammverzeichnis des erstellten Projekts im Dateisystem;
  • Zeile 2: Der Name des virtuellen Servers. Die URL-Dateien für diesen Server haben das Format [http(s)://projet-test.test/chemin];
  • Zeilen 4–12: Konfiguration der virtuellen Website für Port 80 (Zeile 4) und das Protokoll HTTP;
  • Zeilen 14–27: Konfiguration der virtuellen Website für Port 443 (Zeile 14) und das Protokoll HTTPS;

Schauen wir uns an, wie ein virtueller Server funktioniert. Starten wir zunächst den Apache-Server und PHP:

Anschließend rufen wir mit einem Browser die Seite URL [http://projet-test.test/] auf:

Image

  • in [1], die angeforderte URL;
  • In [2] wurde das Protokoll HTTP verwendet;
  • bei [3] erhält man, da das Projekt [projet-test] leer ist, den Index seines Ordners (Inhaltsverzeichnis), einen leeren Index;

Nun fordern wir das gesicherte URL aus [https://projet-test.test/] an:

Image

  • Bei [1-2] erhalten wir dieselbe Antwort wie zuvor, jedoch mit dem Protokoll HTTPS [1];

Durch die Erstellung des virtuellen Servers [projet-test.test] wurde ein neuer Eintrag in der Datei [<windows>/system32/drivers/etc/hosts] erstellt:

Image

# Copyright (c) 1993–2009 Microsoft Corp.
#
# Dies ist eine Beispieldatei, die von Microsoft TCP/IP für Windows verwendet wird.
#
# Diese Datei enthält die Zuordnungen von IP-Adressen zu Hostnamen. Jeder
# Eintrag sollte in einer eigenen Zeile stehen. Die IP-Adresse sollte
# in der ersten Spalte stehen, gefolgt vom entsprechenden Hostnamen.
# Die IP-Adresse und der Hostname sollten durch mindestens ein
# Leerzeichen getrennt werden.
#
# Darüber hinaus können Kommentare (wie diese) in einzelne
# Zeilen oder im Anschluss an den Maschinennamen, gekennzeichnet durch das Symbol „#“, eingefügt werden.
#
# Beispiel:
#
#       102.54.9    4.97 rhino.         acme.com # Quellserver
#        38.25.    63.10 x             .acme.com # x-Client-Host

# Die Namensauflösung für „localhost“ erfolgt innerhalb von DNS selbst.
#     127.0.0.1       localhost
#     ::1             localhost

127.0.0.1      projet-test.test     #Laragon-Magie!   
  • Zeile 23: Die Adresse IP des Namens [projet-test.test] lautet 127.0.0.1, d. h. die Adresse von [localhost] (Zeile 20), dem lokalen Rechner. Wenn man also in einem Browser die Adresse URL [http://projet-test.test/chemin] eingibt, wird die Anfrage an die Adresse 127.0.0.1 auf Port 80 gesendet. Daraufhin antwortet der Apache-Server des lokalen Rechners (localhost).

Man könnte sich fragen, warum der Apache-Server bei der Eingabe der Anfrage „[http://projet-test.test/]“ die Konfiguration der Datei „[<laragon>\etc\apache2\sites-enabled\auto.projet-test.test.conf]“ verwendet:

Image

Um dies zu verstehen, muss man sich ansehen, was der Browser bei dieser Anfrage an den Apache-Server sendet. Führen wir sie mit Postman durch:

Image

  • Bei [1-3] wird eine Anfrage an HTTPS und [1] gesendet;
  • bei [4] zeigt Postman an, dass das Sicherheitszertifikat nicht erkannt wurde. Das Protokoll HTTPS stellt eine verschlüsselte Verbindung zwischen dem Webclient (hier Postman) und dem Apache-Server her. Diese verschlüsselte Verbindung wird mithilfe von Zertifikaten hergestellt, die zwischen Client und Server ausgetauscht werden. Der Server initiiert den Dialog zum Aufbau der verschlüsselten Verbindung, indem er dem Client ein Sicherheitszertifikat sendet. Damit dieses Zertifikat vom Client akzeptiert wird, muss es signiert sein, d. h. von Unternehmen erworben werden, die zur Ausstellung von Sicherheitszertifikaten berechtigt sind. Als wir das Protokoll HTTPS von Laragon aktiviert haben, hat Laragon das Sicherheitszertifikat selbst erstellt. Man sagt dann, das Zertifikat sei selbstsigniert. Die meisten Web-Clients geben eine Warnung aus, wenn sie ein selbstsigniertes Zertifikat erhalten. Genau das tut Postman bei [4]. Die meisten Web-Clients bieten dann an, die Überprüfung des vom Server gesendeten Sicherheitszertifikats zu deaktivieren. Genau das schlägt Postman bei [5] vor;

Wir klicken auf den Link [5], um die Überprüfung SSL (Secure Sockets Layer) zu deaktivieren. SSL / TSL (Transport Layer Security) ist ein Sicherheitsprotokoll, das einen sicheren Kommunikationskanal zwischen zwei Rechnern im Internet herstellt. Es handelt sich um das hier von Apache verwendete Protokoll. Die Antwort lautet wie folgt:

Image

Wir erhalten dieselbe Seite wie mit einem herkömmlichen Browser. Sehen wir uns nun den Client-Server-Dialog in der Postman-Konsole (Strg-Alt-C) an:

GET / HTTP/1.1
User-Agent: PostmanRuntime/7.26.2
Accept: */*
Cache-Control: no-cache
Postman-Token: d153f711-ad99-4e1d-93c3-61c25374d1be
Host: projet-test.test
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

HTTP/1.1 200 OK
Date: Fri, 14 Aug 2020 09:19:24 GMT
Server: Apache/2.4.35 (Win64) OpenSSL/1.1.1b PHP/7.2.19 mod_wsgi/4.7.1 Python/3.8
Content-Length: 161
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html;charset=UTF-8
  • Zeile 6: Der Header „HTTP [Host]“ gibt den Namen des vom Webclient angeforderten Servers an. Das ist das Prinzip virtueller Server. Unter derselben Adresse IP (hier 127.0.0.1) kann ein Webserver mehrere Websites mit unterschiedlichen Namen hosten. Über den Header HTTP [Host] kann der Client angeben, an welchen Server (hier mit der Adresse 127.0.0.1) er sich wendet;

Was macht Apache nun?

Beim Start liest Apache alle Konfigurationsdateien, die im Ordner [[<laragon>\etc\apache2\sites-enabled]:

Image

Jede Konfigurationsdatei definiert einen virtuellen Server. In der Datei [auto.projet-test.test.conf] findet sich beispielsweise die folgende Zeile:


define ROOT "C:/MyPrograms/laragon/www/projet-test/"
define SITE "projet-test.test"

Zeile 2 definiert den virtuellen Server [projet-test.test]. Die Datei [auto.projet-test.test.conf] enthält die Konfiguration dieses virtuellen Servers. Da der Apache-Server beim Start alle Konfigurationsdateien im Ordner „[<laragon>\etc\apache2\sites-enabled]“ einliest, weiß er, dass es einen virtuellen Server namens „[projet-test.test]“ gibt. Wenn er also vom Postman-Client die Anfrage „HTTPS“ erhält:

1
2
3
4
5
6
7
8
GET / HTTP/1.1
User-Agent: PostmanRuntime/7.26.2
Accept: */*
Cache-Control: no-cache
Postman-Token: d153f711-ad99-4e1d-93c3-61c25374d1be
Host: projet-test.test
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

erkennt er, dass die Anfrage an den virtuellen Server [projet-test.test] (Zeile 6) gerichtet ist und dass dieser existiert. Er verwendet dann die Konfiguration des virtuellen Servers [projet-test.test], um dem Postman-Client zu antworten.

37.4. Erstellung eines ersten virtuellen Apache-Servers

Nachdem wir nun wissen, wozu virtuelle Server dienen und wie man sie definiert, werden wir einen solchen erstellen. Er dient dazu, eine Python-Flask-Anwendung auszuführen, die in einem Ordner namens [Apache] der Version 17 installiert ist, die gerade auf dem Apache-Server bereitgestellt wird:

Wir haben die im Abschnitt |Link| entwickelte Anwendung, einen Webdienst für Datum und Uhrzeit, in den Ordner [http-servers/12/apache/exemple] kopiert:

Image

Der Server [date_time_server.py] lautet wie folgt:


# Importe
import os
import sys
import time

# Wir müssen den Ordner mit den Modulen in den Python-Pfad aufnehmen
# für die Portierung auf Apache unter Windows
sys.path.insert(0"C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages")

from flask import Flask, make_response, render_template

# Flask-Anwendung
script_dir = os.path.dirname(os.path.abspath(__file__))
application = Flask(__name__, template_folder=f"{script_dir}")

# Startseite URL
@application.route('/')
def index():
    # Übermittlung der Uhrzeit an den Client
    # time.localtime: Anzahl der Millisekunden seit dem 01.01.1970
    # time.strftime ermöglicht die Formatierung von Uhrzeit und Datum
    # Format für die Anzeige von Datum und Uhrzeit
    # d: Tag mit 2 Ziffern
    # m: zweistelliger Monat
    # y: Jahr zweistellig
    # H: Stunde 0,23
    # M: Minuten
    # S: Sekunden

    # Datum/Uhrzeit des aktuellen Zeitpunkts
    time_of_day = time.strftime('%d/%m/%y %H:%M:%S', time.localtime())
    # Das an den Kunden zu sendende Dokument wird generiert
    page = {"date_heure": time_of_day}
    document = render_template("date_time_server.html", page=page)
    print("document", type(document), document)
    # Antwort HTTP an den Kunden
    response = make_response(document)
    print("response", type(response), response)
    return response

# nur zur Information
if __name__ == '__main__':
    application.config.update(ENV="development", DEBUG=True)
    application.run()

Die Flask-Anwendung wird über die Kennung [application] referenziert (Zeilen 14, 43, 44). Dieser Name ist obligatorisch. Wenn die Flask-Anwendung mit einer anderen Kennung referenziert wird, funktioniert sie nicht und es erscheint eine Fehlermeldung, die besagt, dass die angeforderte URL nicht gefunden werden kann. Diese Fehlermeldung gibt keinen Hinweis auf die Fehlerquelle. Daher ist in diesem Punkt Vorsicht geboten.

Die in Zeile 34 referenzierte Datei „HTML“ lautet wie folgt:


<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Date et heure du moment</title>
</head>
<body>
    <b>Date et heure du moment : {{page.date_heure}}</b>
</body>
</html>
  • [date-time-server] ist der virtuelle Server, auf dem diese Anwendung gehostet wird. Er wird über die Datei [<laragon>\etc\apache2\sites-enabled\date-time-server.conf] konfiguriert (wir weisen darauf hin, dass dieser Name frei wählbar ist – Apache liest alle in [sites-enabled] vorhandenen Dateien, um die gehosteten virtuellen Websites zu ermitteln);

Wir erstellen diese Datei zunächst durch Kopieren der Datei [auto.projet-test.test.conf] und bearbeiten sie anschließend.

Image

Die Datei „[date-time-server.conf]“ sieht dann wie folgt aus:


# Verzeichnis des Python-Flask-Skripts der Anwendung
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache/exemple"

# Name der durch diese Datei konfigurierten Website
# hier wird sie „date-time-server“ heißen
# Die URL-Einträge haben das Format http(s)://date-time-server/Pfad
define SITE "date-time-server"

# Tragen Sie die Adresse IP 127.0.0.1 für die Website SITE in die Datei c:/windows/system32/drivers/etc/hosts ein

# URL HTTP
<VirtualHost *:80>
    # mit dem Alias / die URL haben die Form http(s)://date-time-server/Pfad/...
    WSGIScriptAlias / "${ROOT}/date_time_server.py"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

# URL, gesichert mit HTTPS
<VirtualHost *:443>
    # mit dem Alias / die URL haben die Form http(s)://date-time-server/Pfad/...
    WSGIScriptAlias / "${ROOT}/date_time_server.py"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>

    SSLEngine on
    SSLCertificateFile      C:/MyPrograms/laragon/etc/ssl/laragon.crt
    SSLCertificateKeyFile   C:/myprograms/laragon/etc/ssl/laragon.key

</VirtualHost>
  • Zeile 7: Wir vergeben einen Namen für den durch die Datei konfigurierten virtuellen Server;
  • Zeile 2: Wir geben den Wert der Variablen [ROOT] an, die in den Zeilen 14 und 27 verwendet wird;
  • Zeilen 14 und 27: Hier wird der Pfad des Python-Skripts angegeben, das ausgeführt werden soll, wenn der virtuelle Server eine Anfrage erhält. Hier wird festgelegt, dass Anfragen für den Server [date-time-server] vom Python-Skript [date_time_server.py] verarbeitet werden. Dieser Unterschied zur Datei [auto.projet-test.test.conf] ergibt sich daraus, dass diese Datei einen Server PHP konfigurierte, während die Datei [date-time-server.conf] einen Python-Server konfiguriert;
  • Zeilen 14 und 27: Das Attribut [WSGIScriptAlias /] gibt hier an, dass das Stammverzeichnis des Servers [date-time-server] [/] sein wird. Somit haben die URL der Anwendung die Form [http(s)://date-time-server/chemin];
  • In den Zeilen 14 und 27 kann der Anwendung ein anderer Stamm zugewiesen werden, zum Beispiel [WSGIScriptAlias /show]. Dann haben die URL der Anwendung die Form [http(s)://show/date-time-server/chemin];

Außerdem müssen wir der Datei [<windows>/system32/drivers/etc/hosts] eine Zeile hinzufügen:

# Copyright (c) 1993–2009 Microsoft Corp.
#
# Dies ist eine Beispiel-HOSTS-Datei, die von Microsoft TCP/IP für Windows verwendet wird.
#
# Diese Datei enthält die Zuordnungen von IP-Adressen zu Hostnamen. Jede
#“ sollte in einer eigenen Zeile stehen. Die Adresse „IP“ sollte
# in der ersten Spalte stehen, gefolgt vom entsprechenden Hostnamen.
# Die Adresse IP und der Hostname sollten durch mindestens ein
# Leerzeichen getrennt werden.
#
# Darüber hinaus können Kommentare (wie diese) in einzelne
# Zeilen oder im Anschluss an den Maschinennamen, gekennzeichnet durch das Symbol „#“, eingefügt werden.
#
# Beispiel:
#
#       102.54.9    4.97 rhino.         acme.com # Quellserver
#        38.25.    63.10 x             .acme.com # x-Client-Host

# Die Namensauflösung für „localhost“ erfolgt innerhalb von DNS selbst.
#     127.0.0.1       localhost
#     ::1             localhost

127.0.0.1    flask-impots-withmysql        # Python-Kurs – Steuer-App mit MySQL
127.0.0.1    flask-impots-withpgres        # Python-Kurs – Steuer-App mit PostgreSQL
127.0.0.1    date-time-server            # Python-Kurs – aktuelle Uhrzeit und Datum
127.0.0.1           projet-test.test                #Laragon Magic!

Wir fügen Zeile 25 hinzu, um dem virtuellen Server [date-time-server] die Adressen IP und [127.0.0.1] zuzuweisen.

Überprüfen wir das Ganze. Wir starten den Apache-Server:

Image

Anschließend rufen wir die Seiten URL und [https://date-time-server] mit einem Browser auf:

Image

  • in [1], die angeforderte Seite URL;
  • in [3] die Antwort des Servers;
  • in [2] zeigt der Browser an, dass die Verbindung HTTPS nicht sicher ist, da er festgestellt hat, dass das vom Apache-Server gesendete Zertifikat selbstsigniert ist;

Fügen wir nun in der Datei [date-time-server.conf] in den Zeilen 14 und 27 einen Alias ein:


    WSGIScriptAlias /show-date-time "${ROOT}/date_time_server.py"

Die Änderung wird vom Apache-Server nicht sofort übernommen. Er muss neu geladen werden:

Image

Anschließend rufen wir die Seiten „URL“ und „[https://date-time-server/show-date-time]“ auf. Die Antwort des Servers lautet dann wie folgt:

Image

37.5. Portierung der Steuerberechnungsanwendung auf Apache / Windows

Der Ordner „[apache] [2]“ wird zunächst durch Kopieren des Ordners „[main]“ erstellt. Es ist wichtig, dass sie sich auf derselben Ebene befinden, damit die Pfade des Skripts [syspath.py], das von [1] nach [2] kopiert wurde, gültig bleiben. Um die funktionierende Anwendung [impots / http-servers/ 12] nicht zu beeinträchtigen, legen wir in [apache] die Konfiguration ab, die vom Apache-Server ausgeführt wird;

Image

  • Die Datei [config] aus [2] ist identisch mit [config] aus [1];
  • Die Datei „[syspath]“ aus „[2]“ ist identisch mit der Datei „[syspath]“ aus „[1]“;
  • Die Datei [main_withmysql] aus [2] ist die Datei [main] aus [1] mit den folgenden Änderungen:

Das Hauptskript [main] erhielt einen Parameter [mysql / pgres], der ihm mitteilte, welches SGBD verwendet werden sollte. Das Skript [main_withmysql] verwendet SGBD und MySQL:


# Es wird ein MySQL- oder PostgreSQL-Parameter erwartet
import os
import sys

# Die Anwendung wird mit MySQL konfiguriert
import config
config = config.configure({'sgbd'"mysql"})

# Abhängigkeiten
from SendAdminMail import SendAdminMail
from Logger import Logger
from ImpôtsError import ImpôtsError

In Zeile 7 wird das SGBD auf MySQL gesetzt.

  • Die Datei [main_withpgres] von [2] ist die Datei [main] von [1] mit folgenden Änderungen: Sie verwendet die Dateien SGBD und PostgreSQL:

# Es wird ein MySQL- oder PostgreSQL-Parameter erwartet
import os
import sys

# Die Anwendung wird mit MySQL konfiguriert
import config
config = config.configure({'sgbd'"pgres"})

# Abhängigkeiten
from SendAdminMail import SendAdminMail
from Logger import Logger
from ImpôtsError import ImpôtsError

In Zeile 7 wird SGBD auf PostgreSQL gesetzt.

Anschließend erstellen Sie das folgende Skript [main_withmysql.wsgi] (die verwendete Endung spielt keine Rolle):

# Verzeichnis dieser Datei
import os
script_dir = os.path.dirname(os.path.abspath(__file__))

# Man fügt sie zum Syspath hinzu, damit der folgende Import möglich ist
import sys
sys.path.insert(0, script_dir)

# Die Flask-Anwendung [app] wird importiert und erhält dabei den Namen [application]
from main_withmysql import app as application

Das Skript [main_withmysql.wsgi] ist das Ziel, das vom Apache-Server im Modus WSGI ausgeführt wird:

  • Das Ziel des Apache-Servers hätte auch das Skript [main_withmysql.py] sein können, wie es zuvor mit dem Skript [date_time_server.py] der Fall war. Allerdings hätte man es dafür leicht anpassen müssen:
    • Im Gegensatz zur Ausführung mit einem Konsolenskript ist bei Apache der Ordner, der das Ziel [main_withmysql.py] enthält, nicht Teil des Python-Pfads. Daher führt Zeile 6 des Skripts [main_withmysql.py] zu einem Fehler;
    • die zweite Änderung, die hätte vorgenommen werden müssen, besteht darin, dass in [main_withmysql] die Flask-Anwendung über die Kennung [app] referenziert wird. Es ist bekannt, dass sie für Apache / WSGI ebenfalls über die Kennung [application] referenziert werden muss;
  • anstatt [main_withmysql.py] zu ändern, ändern wir das Ziel von Apache. Es wird nun das oben genannte Skript [main_withmysql.wsgi] sein:
    • Zeilen 1–7: Der Ordner des Skripts wird in den Python-Pfad aufgenommen. Dadurch löst Zeile 6 von [main_withmysql.py] keinen Fehler mehr aus;
    • Zeilen 9–10: Der Import von [main_withmysql.py] führt zur Ausführung des Skripts. Außerdem wird die in [main_withmysql.py] enthaltene Flask-Anwendung [app] mit der Kennung [application] referenziert, die Apache im Modus WSGI benötigt;

Das Gleiche gilt für das Skript [main_withpgres.wsgi]:


# Ordner dieser Datei
import os
script_dir = os.path.dirname(os.path.abspath(__file__))

# Man fügt sie zum Syspath hinzu, damit der folgende Import möglich ist
import sys
sys.path.insert(0, script_dir)

# Die Flask-Anwendung [app] wird importiert und erhält den Namen [application]
from main_withpgres import app as application

Wir haben nun die ausführbaren Ziele für den Apache-Server. Als Nächstes müssen wir zwei virtuelle Server erstellen, einen für jedes Ziel.

In [<laragon>\etc\apache2\sites-enabled] erstellen wir die Datei [flask-impots-withmysql.conf] (der Name spielt keine Rolle):

Image


# Ordner des .wsgi-Skripts
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"

# Name der durch diese Datei konfigurierten Website
# hier wird sie „flask-impots-withmysql“ heißen
# Die URL-Adressen lauten http(s)://flask-impots-withmysql/Pfad
define SITE "flask-impots-withmysql"

# Tragen Sie die Adresse IP 127.0.0.1 für die Website SITE in die Datei c:/windows/system32/drivers/etc/hosts ein

# Tragen Sie hier die Pfade zu den zu verwendenden Python-Bibliotheken ein – trennen Sie diese durch Kommas
# Hier die Bibliotheken einer virtuellen Python-Umgebung
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"

# Python-Home – nur erforderlich, wenn mehrere Python-Versionen installiert sind
# WSGIPythonHome „C:/Program Files/Python38“

# URL HTTP
<VirtualHost *:80>
    # mit dem Alias / haben die URL die Form /{prefixe_url}/action/...
    # mit dem Alias /impots haben die URL die Form /impots/{prefixe_url}/action/...
    #, wobei [prefixe_url] in parameters.py definiert ist
    WSGIScriptAlias / "${ROOT}/main_withmysql.wsgi"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

# URL, gesichert mit HTTPS
<VirtualHost *:443>
    # mit dem Alias / werden die URL die Form /{prefixe_url}/action/... haben
    # mit dem Alias /impots haben die URL die Form /impots/{prefixe_url}/action/...
    #, wobei [prefixe_url] in parameters.py definiert ist
    WSGIScriptAlias / "${ROOT}/main_withmysql.wsgi"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>

    SSLEngine on
    SSLCertificateFile      C:/MyPrograms/laragon/etc/ssl/laragon.crt
    SSLCertificateKeyFile   C:/myprograms/laragon/etc/ssl/laragon.key

</VirtualHost>
  • Zeile 2: das Stammverzeichnis der Anwendung, der von uns erstellte Ordner „[apache]“;
  • Zeilen 23, 38: das Ziel „[main_witmysql.wsgi]“, das wir erstellt haben:
  • Zeile 7: Der virtuelle Server wird [flask-impots-withmysql] heißen;
  • Zeile 13: Die Anweisung [WSGIPythonPath] ermöglicht es, Ordner zum Python-Pfad hinzuzufügen. Hier weiß Apache nicht, dass wir zur Entwicklung der Anwendung eine virtuelle Umgebung verwendet haben und dass sich alle von der Anwendung verwendeten Module in dieser virtuellen Umgebung befinden. Daher fügen wir in Zeile 13 den Ordner hinzu, der alle Module der verwendeten virtuellen Umgebung enthält. Eine Möglichkeit besteht darin, diesen Ordner an einen anderen Ort im Dateisystem zu kopieren und auf diesen Ort zu verweisen. Eine andere Möglichkeit ist, diesen Ordner direkt im Ziel [main_witmysql.wsgi] zum Python-Pfad hinzuzufügen (dies ist wahrscheinlich die bessere Lösung);
  • Zeile 16: Man kann Apache den Installationsordner von Python im Dateisystem angeben. Normalerweise befindet sich dieser im Verzeichnis PATH des Rechners, und oft ist diese Zeile überflüssig (was hier der Fall war). Es können jedoch mehrere Python-Installationen auf dem Rechner vorhanden sein, und die gewünschte befindet sich nicht im Verzeichnis PATH des Rechners. In diesem Fall löst diese Zeile das Problem;

Auf die gleiche Weise erstellt man eine Datei „[flask-impots-withpgres.conf]“:


# Verzeichnis des .wsgi-Skripts
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"

# Name der durch diese Datei konfigurierten Website
# hier wird sie „flask-impots-withmysql“ heißen
# Die URL-Adressen lauten http(s)://flask-impots-withmysql/Pfad
define SITE "flask-impots-withpgres"

# Tragen Sie die Adresse IP 127.0.0.1 für die Website SITE in die Datei c:/windows/system32/drivers/etc/hosts ein

# Tragen Sie hier die Pfade zu den zu verwendenden Python-Bibliotheken ein – trennen Sie diese durch Kommas
# Hier die Bibliotheken einer virtuellen Python-Umgebung
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"

# Python-Home – nur erforderlich, wenn mehrere Python-Versionen installiert sind
# WSGIPythonHome „C:/Program Files/Python38“

# URL HTTP
<VirtualHost *:80>
    # mit dem Alias / haben die URL die Form /{prefixe_url}/action/...
    # mit dem Alias /impots haben die URL die Form /impots/{prefixe_url}/action/...
    #, wobei [prefixe_url] in parameters.py definiert ist
    WSGIScriptAlias / "${ROOT}/main_withpgres.wsgi"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

# URL, gesichert mit HTTPS
<VirtualHost *:443>
    # mit dem Alias / werden die URL die Form /{prefixe_url}/action/... haben
    # mit dem Alias /impots haben die URL die Form /impots/{prefixe_url}/action/...
    #, wobei [prefixe_url] in parameters.py definiert ist
    WSGIScriptAlias / "${ROOT}/main_withpgres.wsgi"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>

    SSLEngine on
    SSLCertificateFile      C:/MyPrograms/laragon/etc/ssl/laragon.crt
    SSLCertificateKeyFile   C:/myprograms/laragon/etc/ssl/laragon.key

</VirtualHost>

Wir speichern alle diese Dateien, starten den Apache-Server sowie die Dateien SGBD, MySQL und PostgreSQL. Die Anwendung ist mit den Präfixen URL, [/do] und [with_csrftoken=False] (kein Token CSRF) in [configs/parameters.py]. Wir fragen nach URL und [https://flask-impots-withmysql/do]. Die Antwort des Servers lautet wie folgt:

Image

Wir fragen nun nach URL und [https://flask-impots-pgres/do]. Die Antwort lautet wie folgt:

Image

Beide Anwendungen funktionieren normal.

Ändern wir nun den Parameter [WSGIScriptAlias] in [flask-impots-withmysql.conf]:


# Verzeichnis des .wsgi-Skripts
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"



# URL HTTP
<VirtualHost *:80>
    # mit dem Alias / haben die URL die Form /{prefixe_url}/action/...
    # mit dem Alias /impots haben die URL die Form /impots/{prefixe_url}/action/...
    #, wobei [prefixe_url] in parameters.py definiert ist
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …
</VirtualHost>

# URL, gesichert mit HTTPS
<VirtualHost *:443>
    # mit dem Alias / die URL haben die Form /{prefixe_url}/action/...
    # mit dem Alias /impots haben die URL die Form /impots/{prefixe_url}/action/...
    #, wobei [prefixe_url] in parameters.py definiert ist
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …

</VirtualHost>
  • In den Zeilen 11 und 20 lautet der Alias WSI nun [/impots];

Wir stoppen und starten den Apache-Server neu und fragen dann nach URL und [https://flask-impots-withmysql/impots/do]. Die Antwort des Servers lautet wie folgt:

Image

Es liegt ein Absturz vor. Die Fehlermeldung URL [1] gibt uns die Ursache dafür an. Sie hätte [https://flask-impots-withmysql/impots/do/afficher-vue-authentification] lauten müssen. Der Alias WSGI fehlt. Das ist ein Fehler unserer Anwendung. Sie kann ein Präfix von URL verarbeiten (/do ist vorhanden). Man könnte meinen, dass das Hinzufügen des Präfixes [/impots/do] zu unserer Anwendung das vorherige Problem lösen würde. Aber nein. Dann treten andere Arten von Problemen auf. Der Alias WSGI verhält sich nicht wie ein Präfix von URL.

Versuchen wir zu verstehen, was passiert ist. Wir haben URL und [https://flask-impots-withmysql/impots/do] angefordert. Wir hatten erwartet, die Authentifizierungsansicht zu erhalten. Bei [1] ist oben zu sehen, dass die Anwendung deren Anzeige angefordert hat, jedoch nicht mit dem richtigen URL. Sehen wir uns den Ablauf der Anfrage für [https://flask-impots-withmysql/impots/do] an.

Zunächst wurde die folgende Route (configs/routes.py) ausgeführt:


    # die Routen der Flask-Anwendung
    # Stammverzeichnis der Anwendung
    app.add_url_rule(f'{prefix_url}/', methods=['GET'],
                     view_func=routes.index)

Die Route in Zeile 3 lautet in unserem Beispiel [https://flask-impots-withmysql/impots/do]. Man sieht, dass der Teil „[https://flask-impots-withmysql/impots]“ aus der Route entfernt wurde, sodass nun einfach „[/do]“ übrig bleibt. Was den Teil „[https://flask-impots-withmysql]“ betrifft, ist dies normal, da der Servername nicht in der Route enthalten ist. Man sieht jedoch, dass auch der Alias WSGI [/impots] nicht übernommen wird. Das ist ein wichtiger Punkt. Selbst mit einem Alias wie WSGI bleiben unsere ursprünglichen Routen gültig.

Schauen wir uns nun an, was die Funktion [index] in Zeile 4 (configs/routes_without_csrftoken) tut:


# Stammverzeichnis der Anwendung
def index() -> tuple:
    # Weiterleitung zu /init-session/html
    return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)

In Zeile 4 werden wir zur Funktion URL der Funktion [init_session] weitergeleitet. In [configs/routes.py] wurde diese Funktion der Route [/do/init-session/html] zugeordnet:


    # init-session
    app.add_url_rule(f'{prefix_url}/init-session/<string:type_response>{csrftoken_param}', methods=['GET'],
                     view_func=routes.init_session)

Zeile 2, in unserem Test [csrftoken_param], ist die leere Zeichenkette. Die Anwendung unterstützt hier kein Token CSRF.

Die Funktion [init_session] ist wie folgt definiert (configs/routes_without_csrftoken):


# init-session
def init_session(type_response: str) -> tuple:
    # Der zur Aktion gehörende Controller wird ausgeführt
    return front_controller()

In Zeile 4 beginnt die Verarbeitungskette der Aktion [init-session]. Diese Kette endet wie folgt in [responses/HtmlResponse]:



# Nun muss die Weiterleitungs-ID URL generiert werden, wobei das Token CSRF nicht vergessen werden darf, falls es angefordert wird
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

        # Weiterleitungsantwort
        return redirect(f"{config['parameters']['prefix_url']}{ads['to']}{csrf_token}"), status.HTTP_302_FOUND

Die Aktion [init-session] ist eine Aktion vom Typ ADS („Action Do Something“), die mit einer Weiterleitung zu einer Ansicht endet (Zeile 9). Genau hier liegt das Problem. Die Funktion [redirect] in Zeile 9 fügt den Alias WSGI nicht automatisch zur Weiterleitungsfunktion URL hinzu. Dies zeigt der obige Screenshot. Im Umleitungsobjekt „URL“ fehlt der Alias „/impots“.

Die folgende Version bietet eine Lösung für das Problem mit dem Alias „WSGI“.