Skip to content

37. Toepassingsopdracht: versie 17

Image

Deze nieuwe versie brengt de volgende wijzigingen met zich mee:

  • de versie wordt overgezet naar een Apache/Windows-server;
  • om deze port te realiseren, bevat versie 17 alle benodigde afhankelijkheden in de map [impots / http-servers/ 12]. Ter herinnering: eerdere versies haalden hun afhankelijkheden uit verschillende mappen binnen het gehele project [python-flask-2020];

37.1. Verplaatsing van de afhankelijkheden van de applicatie

Image

Ter herinnering: het beheer van de afhankelijkheden van de applicatie vindt plaats in het script [syspath]. In de vorige versie zag dit script er als volgt uit:


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

    # map van dit bestand
    script_dir = os.path.dirname(os.path.abspath(__file__))

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

    # afhankelijkheden
    absolute_dependencies = [
        # projectmappen
        # 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",
        # Constanten, segmenten
        f"{root_dir}/impots/v05/entities",
        # Logger, SendAdminMail
        f"{root_dir}/impots/http-servers/02/utilities",
        # map van het hoofdscript
        script_dir,
        # configuraties [database, layers, parameters, controllers, views]
        f"{script_dir}/../configs",
        # controllers
        f"{script_dir}/../controllers",
        # antwoorden HTTP
        f"{script_dir}/../responses",
        # weergavemodellen
        f"{script_dir}/../models_for_views",
    ]

    # het syspath instellen
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # de configuratie wordt weergegeven
    return {
        "root_dir": root_dir,
        "script_dir": script_dir
    }

Alle afhankelijkheden waarvan de absolute naam afhankelijk is van de variabele [root_dir] op regel 8, d.w.z. de regels 13-26, moeten worden verplaatst.

Het script [syspath] van de nieuwe versie ziet er als volgt uit:


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

    # map van dit bestand
    script_dir = os.path.dirname(os.path.abspath(__file__))

    # afhankelijkheden
    absolute_dependencies = [
        # entiteiten van de applicatie
        f"{script_dir}/../entities",
        # laag [dao]
        f"{script_dir}/../layers/dao",
        # laag [métier]
        f"{script_dir}/../layers/métier",
        # hulpprogramma's
        f"{script_dir}/../utilities",
        # map met het hoofdscript
        script_dir,
        # configuraties [database, layers, parameters, controllers, views]
        f"{script_dir}/../configs",
        # controllers
        f"{script_dir}/../controllers",
        # antwoorden HTTP
        f"{script_dir}/../responses",
        # weergavemodellen
        f"{script_dir}/../models_for_views",
    ]

    # het syspath instellen
    import sys
    # de absolute afhankelijkheden van het project toevoegen
    for directory in absolute_dependencies:
        # controleert of de map bestaat
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            # de ontwikkelaar wordt gewaarschuwd
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            # de map wordt aan het begin van de syspath toegevoegd
            sys.path.insert(0, directory)

    # de configuratie wordt doorgevoerd
    return {
        "script_dir": script_dir,
    }
  • regels 8-27: alle afhankelijkheden zijn nu relatief ten opzichte van de variabele [script_dir] op regel 5;
  • regels 42-45: de variabele [root_dir] is verdwenen uit de syspath-configuratie;
  • regel 10: de entiteiten van de applicatie bevinden zich in de map [entities] [1];
  • regel 12: de laag [dao] bevindt zich in de map [layers/dao] [2];
  • regel 14: de laag [métier] bevindt zich in de map [layers/métier] [2];
  • regel 16: de hulpprogramma's [Logger, SendMail] bevinden zich in de map [utilities] [3];
  • regels 29-40: het Python-pad van de toepassing wordt berekend zonder de module [myutils] te importeren;

Image

37.2. Tests

In dit stadium zou versie 17 moeten werken. Controleer dit.

37.3. Porten van een Python-/Flask-toepassing naar een Apache-/Windows-server

37.3.1. Bronnen

Om een Flask-applicatie naar Apache / Windows te porten, moest ik op internet zoeken. Hier is de link die me op weg heeft geholpen: [https://medium.com/@madumalt/flask-app-deployment-in-windows-apache-server-mod-wsgi-82e1cfeeb2ed];

Ik heb de informatie uit deze link gebruikt, behalve voor de configuratie van de Apache-server. Hiervoor heb ik een voorbeeld van een Apache-serverconfiguratie van Laragon gebruikt.

37.3.2. Installatie van de Python-module mod_wsgi

De Python/Flask-applicatie die we hebben ontwikkeld, maakte gebruik van de WSGI (Web Server Gateway Interface) [werkzeug]-server die bij Flask wordt meegeleverd. Deze server wordt |hier| beschreven. De link [https://www.fullstackpython.com/wsgi-servers.html] beschrijft de werking van WSGI-servers. Er bestaan verschillende |WSGI-servers|. Een daarvan is de Apache-server die in de WSGI-modus draait. Dit is de oplossing die we hier hebben gekozen, omdat Laragon, dat we hebben geïnstalleerd, wordt geleverd met een Apache-server.

Om ervoor te zorgen dat de Apache-server een Python-toepassing kan hosten, moeten we de Python-module [mod_wsgi] installeren. De installatie van deze module is lastig omdat er tijdens de installatie een C++-compilatie plaatsvindt. Om de installatie te laten slagen, is een Microsoft C++-compiler nodig. Een eenvoudige oplossing is het installeren van de huidige versie van Visual Studio Community [https://visualstudio.microsoft.com/fr/vs/community/].

Als je Visual Studio alleen nodig hebt voor [mod_wsgi], kun je de installatie beperken tot de C++-omgeving:

Image

Zodra de C++-compiler is geïnstalleerd, kun je de module [mod_wsgi] installeren in een 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
  • regel 1: we stellen de waarde van de omgevingsvariabele [MOD_WSGI_APACHE_ROOTDIR] in. Deze waarde is de locatie van de Apache-server in het bestandssysteem. Hier is die locatie [<laragon>\bin\apache\httpd-2.4.35-win64-VC15], waarbij <laragon> de installatiemap van Laragon is. U kunt deze locatie op verschillende manieren achterhalen. Hier volgt een voorbeeld dat is verkregen via een van de opties van Laragon:

Image

In [1-3] is het bestand [httpd.conf] het hoofdconfiguratiebestand van de Apache-server. Dit bestand wordt vervolgens geopend in een teksteditor (hieronder Notepad++):

Image

In [2] is de installatiemap van Apache het gedeelte dat voorafgaat aan de tekenreeks [conf\httpd.conf].

Laten we teruggaan naar de installatie van de module [mod_wsgi]:

  • regels 3-9: installatie van de module [mod_wsgi];

37.3.3. Configuratie van de Apache-server van Laragon

We gaan de Apache-server van Laragon configureren. We beginnen met het hoofdconfiguratiebestand [httpd.conf]:

Image

We gaan naar het einde van het bestand [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"
  • regel 8 is toegevoegd aan het bestaande bestand [httpd.conf] aan het einde van het bestand. Deze regel geeft de Apache-server aan waar een onderdeel van de module [mod_wsgi] zich bevindt, die we zojuist hebben geïnstalleerd;

Een eenvoudige manier om het pad van regel 8 te achterhalen, is door de volgende opdracht uit te voeren in een terminal 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 sommige documentatie staat dat de regels 2 en 3 aan het einde van het bestand [httpd.conf] moeten worden toegevoegd. In mijn geval veroorzaakte regel 3 hierboven een fout (module [encodings] ontbreekt). Daarom is deze niet in het bestand [httpd.conf] opgenomen. Alleen regel 2 is daarin opgenomen. De betekenis van de verschillende parameters van de module [mod_wsgi] die in de configuratiebestanden van Apache kunnen worden gebruikt, wordt |hier| beschreven.

Vervolgens gaan we het protocol HTTPS van de Apache-server activeren:

Image

  • in [1-4] activeren we het protocol HTTPS van de Apache-server;

Vanaf nu kunnen we URL en [https://serveur/chemin] gebruiken;

Om Apache zo te configureren dat het een Flask-applicatie kan hosten, maakt de |hierboven| genoemde link gebruik van virtuele servers. Laragon biedt ook de mogelijkheid om virtuele servers te beheren:

Image

  • in [1-3] vragen we Laragon om automatisch virtuele hosts aan te maken;

De volgende stap is het aanmaken van een webproject met Laragon:

 
  • in [1-3] wordt een leeg project PHP aangemaakt;
  • in [4-8], heeft Laragon een virtuele site aangemaakt met de naam [auto.projet-test.test], geconfigureerd door het bestand [auto.projet-test.test.conf] [8] in de map [sites-enabled] [7]. Deze map bevindt zich op het adres [<laragon>\etc\apache2\sites-enabled], waarbij [laragon] de installatiemap van Laragon is;

Hoewel dit niet direct te maken heeft met wat we op dit moment doen, bent u misschien wel nieuwsgierig om eens te kijken wat die website [projet-test] is die we zojuist hebben aangemaakt:

Image

  • In [1-5] is een leeg project aangemaakt. Dit is een project met de naam PHP dat zich bevindt in de map [<laragon>/www], waarbij [laragon] de installatiemap van Laragon is;

Laten we nu eens kijken naar het bestand [auto.projet-test.test.conf] dat door Laragon is gegenereerd in de map [<laragon>\etc\apache2\sites-enabled]:


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>
  • regel 1: de root van het aangemaakte project in het bestandssysteem;
  • regel 2: de naam van de virtuele server. De URL-bestanden voor deze server zullen de vorm [http(s)://projet-test.test/chemin] hebben;
  • regels 4-12: configuratie van de virtuele site voor poort 80 (regel 4) en het protocol HTTP;
  • regels 14-27: configuratie van de virtuele site voor poort 443 (regel 14) en het protocol HTTPS;

Laten we eens kijken hoe een virtuele server werkt. Laten we eerst de Apache-server en PHP starten:

Vervolgens roepen we met een browser de URL [http://projet-test.test/] op:

Image

  • in [1], de opgevraagde URL;
  • in [2] is het protocol HTTP gebruikt;
  • in [3], omdat het project [projet-test] leeg is, krijgen we de index van de map (inhoudsopgave), een lege index;

Laten we nu de beveiligde URL opvragen via [https://projet-test.test/]:

Image

  • in [1-2] krijgen we hetzelfde antwoord als eerder, maar met het protocol HTTPS [1];

Door het aanmaken van de virtuele server [projet-test.test] is er een nieuwe vermelding toegevoegd aan het bestand [<windows>/system32/drivers/etc/hosts]:

Image

# Copyright (c) 1993-2009 Microsoft Corp.
#
# Dit is een voorbeeldbestand dat wordt gebruikt door Microsoft HOSTS/TCP/IP voor Windows.
#
# Dit bestand bevat de toewijzingen van IP-adressen aan hostnamen. Elke
# vermelding moet op een aparte regel staan. Het IP-adres moet
# in de eerste kolom worden geplaatst, gevolgd door de bijbehorende hostnaam.
# Het IP-adres en de hostnaam moeten worden gescheiden door ten minste één
# spatie.
#
# Bovendien kunnen opmerkingen (zoals deze) worden ingevoegd op afzonderlijke
# regels of na de machinenaam, aangeduid met het symbool '#'.
#
# Bijvoorbeeld:
#
#       102.54.9    4.97 rhino.         acme.com # bronserver
#        38.25.    63.10 x             .acme.com # x-clienthost

# de naamomzetting van localhost wordt binnen DNS zelf afgehandeld.
#     127.0.0.1       localhost
#     ::1             localhost

127.0.0.1      projet-test.test     #laragon-magie!   
  • regel 23: het adres IP van de naam [projet-test.test] is 127.0.0.1, d.w.z. het adres van [localhost] (regel 20), de lokale machine. Wanneer men dus in een browser URL [http://projet-test.test/chemin] invoert, wordt het verzoek verzonden naar het adres 127.0.0.1 op poort 80. Vervolgens reageert de Apache-server van de lokale machine (localhost).

Je kunt je afvragen waarom de Apache-server, wanneer je het verzoek [http://projet-test.test/] invoert, de configuratie uit het bestand [<laragon>\etc\apache2\sites-enabled\auto.projet-test.test.conf] gebruikt:

Image

Om dit te begrijpen, moeten we kijken wat de browser naar de Apache-server stuurt wanneer we dit verzoek doen. Laten we dit doen met Postman:

Image

  • in [1-3] sturen we een verzoek HTTPS [1];
  • bij [4] geeft Postman aan dat het het beveiligingscertificaat niet heeft herkend. Het protocol HTTPS brengt een versleutelde verbinding tot stand tussen de webclient (hier Postman) en de Apache-server. Deze versleutelde verbinding komt tot stand door middel van certificaten die tussen de client en de server worden uitgewisseld. Het is de server die het dialoogvenster voor het tot stand brengen van de versleutelde verbinding initieert door een beveiligingscertificaat naar de client te sturen. Om door de client te worden geaccepteerd, moet dit certificaat ondertekend zijn, oftewel aangeschaft bij bedrijven die bevoegd zijn om beveiligingscertificaten uit te geven. Toen we het protocol HTTPS van Laragon activeerden, heeft Laragon het beveiligingscertificaat zelf aangemaakt. We zeggen dan dat het certificaat zelfondertekend is. De meeste webclients geven een waarschuwing weer wanneer ze een zelfondertekend certificaat ontvangen. Dit is wat Postman doet in [4]. De meeste webclients bieden dan de mogelijkheid om de verificatie van het door de server verzonden beveiligingscertificaat uit te schakelen. Dit is wat Postman voorstelt in [5];

We klikken op de link [5] om de verificatie SSL (Secure Sockets Layer) uit te schakelen. SSL / TSL (Transport Layer Security) is een beveiligingsprotocol dat een beveiligd communicatiekanaal tot stand brengt tussen twee computers op het internet. Dit is het protocol dat hier door Apache wordt gebruikt. Het antwoord is als volgt:

Image

We ontvangen dezelfde pagina als met een traditionele browser. Laten we nu eens kijken naar de client/server-dialoog in de Postman-console (Ctrl-Alt-C):

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
  • regel 6: de header HTTP [Host] geeft de naam aan van de server waarop de webclient zich richt. Dit is het principe van virtuele servers. Op hetzelfde adres IP (hier 127.0.0.1) kan een webserver meerdere websites met verschillende namen hosten. Met de header HTTP [Host] kan de client aangeven naar welke server (hier met het adres 127.0.0.1) hij zich richt;

Wat doet Apache nu?

Bij het opstarten leest Apache alle configuratiebestanden die zich in de map [[<laragon>\etc\apache2\sites-enabled]] bevinden:

Image

Elk configuratiebestand definieert een virtuele server. In het bestand [auto.projet-test.test.conf] staat bijvoorbeeld de volgende regel:


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

Regel 2 definieert de virtuele server [projet-test.test]. Het bestand [auto.projet-test.test.conf] bevat de configuratie van deze virtuele server. Omdat de Apache-server bij het opstarten alle configuratiebestanden in de map [<laragon>\etc\apache2\sites-enabled] leest, weet hij dat er een virtuele server bestaat met de naam [projet-test.test]. Wanneer hij dus van de Postman-client het verzoek HTTPS ontvangt:

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

dan herkent hij dat het verzoek gericht is aan de virtuele server [projet-test.test] (regel 6) en dat deze bestaat. Vervolgens gebruikt hij de configuratie van de virtuele server [projet-test.test] om de Postman-client te beantwoorden.

37.4. Een eerste virtuele Apache-server aanmaken

Nu we weten waar virtuele servers voor dienen en hoe we ze kunnen instellen, gaan we er een aanmaken. Deze zal worden gebruikt om een Python Flask-applicatie uit te voeren die is geïnstalleerd in de map [Apache] van versie 17, die momenteel wordt geïmplementeerd op de Apache-server:

We hebben in de map [http-servers/12/apache/exemple] de in paragraaf |link| ontwikkelde applicatie geplaatst, een webdienst voor datum en tijd:

Image

De server [date_time_server.py] is als volgt:


# imports
import os
import sys
import time

# we moeten de map met modules aan het Python-pad toevoegen
# voor porting naar Apache op 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-applicatie
script_dir = os.path.dirname(os.path.abspath(__file__))
application = Flask(__name__, template_folder=f"{script_dir}")

# Home URL
@application.route('/')
def index():
    # tijd naar de client verzenden
    # time.localtime: aantal milliseconden sinds 01/01/1970
    # time.strftime maakt het mogelijk om de tijd en datum op te maken
    # weergaveformaat voor datum en tijd
    # d: dag met 2 cijfers
    # m: maand in twee cijfers
    # y: jaar met 2 cijfers
    # H: uur 0,23
    # M: minuten
    # S: seconden

    # datum/tijdstip
    time_of_day = time.strftime('%d/%m/%y %H:%M:%S', time.localtime())
    # het document dat naar de klant moet worden verzonden, wordt gegenereerd
    page = {"date_heure": time_of_day}
    document = render_template("date_time_server.html", page=page)
    print("document", type(document), document)
    # antwoord HTTP aan de klant
    response = make_response(document)
    print("response", type(response), response)
    return response

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

De Flask-applicatie wordt aangeduid met de ID [application] (regels 14, 43, 44). Deze naam is verplicht. Als de Flask-applicatie met een andere ID wordt aangeroepen, zal de applicatie niet werken en verschijnt er een foutmelding waarin staat dat de gevraagde URL niet kan worden gevonden. Deze foutmelding geeft geen indicatie over de oorzaak van de fout. Wees hier dus voorzichtig mee.

Het bestand HTML waarnaar in regel 34 wordt verwezen, is het volgende:


<!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] is de virtuele server waarop deze applicatie wordt gehost. Deze wordt geconfigureerd via het bestand [<laragon>\etc\apache2\sites-enabled\date-time-server.conf] (ter herinnering: deze naam is vrij te kiezen – Apache leest alle bestanden in [sites-enabled] om de gehoste virtuele sites te identificeren);

We verkrijgen dit bestand eerst door het bestand [auto.projet-test.test.conf] te kopiëren en vervolgens aan te passen.

Image

Het bestand [date-time-server.conf] ziet er als volgt uit:


# map van het Python-Flask-script van de applicatie
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache/exemple"

# naam van de website die door dit bestand is geconfigureerd
# hier heet deze ‘date-time-server’
# de URL zullen van het type http(s)://date-time-server/pad zijn
define SITE "date-time-server"

# voeg het adres IP 127.0.0.1 toe voor de site SITE in c:/windows/system32/drivers/etc/hosts

# URL HTTP
<VirtualHost *:80>
    # met de alias / de URL zullen de vorm http(s)://date-time-server/pad/... hebben
    WSGIScriptAlias / "${ROOT}/date_time_server.py"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

# URL beveiligd met HTTPS
<VirtualHost *:443>
    # met de alias / de URL zullen de vorm http(s)://date-time-server/pad/... hebben
    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>
  • regel 7: we geven de virtuele server die door het bestand wordt geconfigureerd een naam;
  • regel 2: we geven de waarde op van de variabele [ROOT] die op de regels 14 en 27 wordt gebruikt;
  • regels 14 en 27: hier wordt het pad aangegeven naar het Python-script dat moet worden uitgevoerd wanneer de virtuele server een verzoek ontvangt. Hier wordt aangegeven dat verzoeken voor de server [date-time-server] worden verwerkt door het Python-script [date_time_server.py]. Dit verschil met het bestand [auto.projet-test.test.conf] komt doordat dat bestand een server PHP configureerde, terwijl het bestand [date-time-server.conf] een Python-server configureert;
  • regels 14 en 27: het attribuut [WSGIScriptAlias /] geeft hier aan dat de root van de server [date-time-server] [/] zal zijn. De URL-bestanden van de applicatie krijgen dus de vorm [http(s)://date-time-server/chemin];
  • in de regels 14 en 27 kunnen we de applicatie een andere root toewijzen, bijvoorbeeld [WSGIScriptAlias /show]. Dan krijgen de URL van de applicatie de vorm [http(s)://show/date-time-server/chemin];

We moeten ook een regel toevoegen aan het bestand [<windows>/system32/drivers/etc/hosts]:

# Copyright (c) 1993-2009 Microsoft Corp.
#
# Dit is een voorbeeldbestand van het type HOSTS dat wordt gebruikt door Microsoft TCP/IP voor Windows.
#
# Dit bestand bevat de toewijzingen van IP-adressen aan hostnamen. Elk
#moet op een aparte regel worden geplaatst. Het adres IP moet
# in de eerste kolom worden geplaatst, gevolgd door de bijbehorende hostnaam.
# Het adres IP en de hostnaam moeten worden gescheiden door ten minste één
# spatie.
#
# Bovendien kunnen opmerkingen (zoals deze) worden ingevoegd op afzonderlijke
# regels of na de machinenaam, aangeduid met het symbool '#'.
#
# Bijvoorbeeld:
#
#       102.54.9    4.97 rhino.         acme.com # bronserver
#        38.25.    63.10 x             .acme.com # x-clienthost

# de naamomzetting voor localhost wordt binnen DNS zelf afgehandeld.
#     127.0.0.1       localhost
#     ::1             localhost

127.0.0.1    flask-impots-withmysql        # Python-cursus - belastingapp met MySQL
127.0.0.1    flask-impots-withpgres        # Python-cursus - belastingapp met PostgreSQL
127.0.0.1    date-time-server            # Python-cursus - huidige tijd en datum
127.0.0.1           projet-test.test                #Laragon Magic!

We voegen regel 25 toe, om het adres IP [127.0.0.1] toe te wijzen aan de virtuele server [date-time-server].

Laten we dit allemaal even controleren. We starten de Apache-server:

Image

Vervolgens vragen we de pagina URL [https://date-time-server] op via een browser:

Image

  • in [1], de opgevraagde URL;
  • in [3], het antwoord van de server;
  • in [2] geeft de browser aan dat de verbinding HTTPS niet veilig is, omdat hij heeft gedetecteerd dat het door de Apache-server verzonden certificaat zelfondertekend was;

Laten we nu in het bestand [date-time-server.conf] op regel 14 en 27 een alias toevoegen:


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

De wijziging wordt niet onmiddellijk door de Apache-server overgenomen. Je moet de server opnieuw laden:

Image

Vervolgens vragen we de URL [https://date-time-server/show-date-time] op. Het antwoord van de server is dan als volgt:

Image

37.5. Porting van de belastingberekeningsapplicatie naar Apache / Windows

Het bestand [apache] [2] wordt in eerste instantie verkregen door het bestand [main] te kopiëren. Het is belangrijk dat ze zich op hetzelfde niveau bevinden, zodat de paden van het script [syspath.py], dat van [1] naar [2] is gekopieerd, geldig blijven. Om de goed werkende applicatie [impots / http-servers/ 12] niet te verstoren, plaatsen we in [apache] de configuratie die door de Apache-server zal worden uitgevoerd;

Image

  • het bestand [config] van [2] is hetzelfde als [config] van [1];
  • het bestand [syspath] van [2] is hetzelfde als [syspath] van [1];
  • het bestand [main_withmysql] van [2] is het bestand [main] van [1] met de volgende wijzigingen:

Het hoofdscript [main] ontving een parameter [mysql / pgres] die aangaf welk SGBD moest worden gebruikt. Het script [main_withmysql] gebruikt SGBD en MySQL:


# er wordt een MySQL- of PostgreSQL-parameter verwacht
import os
import sys

# de applicatie wordt geconfigureerd met MySQL
import config
config = config.configure({'sgbd'"mysql"})

# afhankelijkheden
from SendAdminMail import SendAdminMail
from Logger import Logger
from ImpôtsError import ImpôtsError

Op regel 7 wordt de SGBD ingesteld op MySQL.

  • het bestand [main_withpgres] van [2] is het bestand [main] van [1] met de volgende wijzigingen: het maakt gebruik van SGBD en PostgreSQL:

# er wordt een MySQL- of PostgreSQL-parameter verwacht
import os
import sys

# de applicatie wordt geconfigureerd met MySQL
import config
config = config.configure({'sgbd'"pgres"})

# afhankelijkheden
from SendAdminMail import SendAdminMail
from Logger import Logger
from ImpôtsError import ImpôtsError

Op regel 7 wordt SGBD ingesteld op PostgreSQL.

Vervolgens maakt men het volgende script [main_withmysql.wsgi] (het gebruikte achtervoegsel doet er niet toe):

# map van dit bestand
import os
script_dir = os.path.dirname(os.path.abspath(__file__))

# we voegen het toe aan de syspath zodat de volgende import mogelijk is
import sys
sys.path.insert(0, script_dir)

# de Flask-toepassing [app] wordt geïmporteerd en krijgt de naam [application]
from main_withmysql import app as application

Het script [main_withmysql.wsgi] is het doel dat door de Apache-server wordt uitgevoerd in de modus WSGI:

  • het doel van de Apache-server had ook het script [main_withmysql.py] kunnen zijn, zoals eerder is gedaan met het script [date_time_server.py]. Maar dan had het enigszins aangepast moeten worden:
    • in tegenstelling tot de uitvoeringsmodus met een consolescript, maakt bij Apache de map met het doel [main_withmysql.py] geen deel uit van het Python Path. Daardoor veroorzaakt regel 6 van het script [main_withmysql.py] een fout;
    • de tweede wijziging die nodig zou zijn geweest, is dat in [main_withmysql] naar de Flask-applicatie wordt verwezen met de identificatiecode [app]. We weten dat deze voor Apache / WSGI ook moet worden aangeduid met de identificatiecode [application];
  • in plaats van [main_withmysql.py] te wijzigen, passen we het doel van Apache aan. Dit wordt voortaan het bovenstaande script [main_withmysql.wsgi]:
    • regels 1-7: we voegen de map van het script toe aan het Python Path. Daardoor veroorzaakt regel 6 van [main_withmysql.py] geen fout meer;
    • regels 9-10: het importeren van [main_withmysql.py] zorgt ervoor dat het wordt uitgevoerd. Bovendien verwijzen we naar de Flask-applicatie [app], die zich in [main_withmysql.py] bevindt, met de identificatiecode [application] die Apache nodig heeft in de modus WSGI;

Hetzelfde geldt voor het script [main_withpgres.wsgi]:


# map van dit bestand
import os
script_dir = os.path.dirname(os.path.abspath(__file__))

# we voegen het toe aan de syspath zodat de volgende import mogelijk is
import sys
sys.path.insert(0, script_dir)

# we importeren de Flask-applicatie [app] en geven deze de naam [application]
from main_withpgres import app as application

We hebben nu de uitvoerbare bestanden voor de Apache-server. We moeten nu twee virtuele servers aanmaken, één voor elk bestand.

In [<laragon>\etc\apache2\sites-enabled] maken we het bestand [flask-impots-withmysql.conf] aan (de naam maakt niet uit):

Image


# map van het .wsgi-script
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"

# naam van de website die door dit bestand wordt geconfigureerd
# hier zal deze flask-impots-withmysql heten
# de URL zullen van het type http(s)://flask-impots-withmysql/pad zijn
define SITE "flask-impots-withmysql"

# voeg het adres IP 127.0.0.1 voor de site SITE toe aan c:/windows/system32/drivers/etc/hosts

# voeg hier de paden toe naar de te gebruiken Python-bibliotheken – scheid ze met komma’s
# hier de bibliotheken van een virtuele Python-omgeving
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"

# Python Home – alleen nodig als er meerdere Python-versies zijn geïnstalleerd
# WSGIPythonHome "C:/Program Files/Python38"

# URL HTTP
<VirtualHost *:80>
    # met de alias / zullen de URL de vorm /{prefixe_url}/action/... hebben
    # met de alias /impots zullen de URL de vorm /impots/{prefixe_url}/action/... aannemen
    # waarbij [prefixe_url] is gedefinieerd in parameters.py
    WSGIScriptAlias / "${ROOT}/main_withmysql.wsgi"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

# URL beveiligd met HTTPS
<VirtualHost *:443>
    # met de alias / zullen de URL de vorm /{prefixe_url}/action/... hebben
    # met de alias /impots zullen de URL de vorm /impots/{prefixe_url}/action/... aannemen
    # waarbij [prefixe_url] is gedefinieerd in parameters.py
    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>
  • regel 2: de root van de applicatie, de map [apache] die we hebben aangemaakt;
  • regels 23, 38: het doel [main_witmysql.wsgi] dat we hebben aangemaakt:
  • regel 7: de virtuele server krijgt de naam [flask-impots-withmysql];
  • regel 13: met de richtlijn [WSGIPythonPath] kunnen mappen aan het Python Path worden toegevoegd. Apache weet hier niet dat we een virtuele omgeving hebben gebruikt om de applicatie te ontwikkelen en dat alle modules die door de applicatie worden gebruikt, zich in deze virtuele omgeving bevinden. Daarom voegen we op regel 13 de map toe die alle modules van de gebruikte virtuele omgeving bevat. Een mogelijkheid is om deze map naar een andere locatie in het bestandssysteem te kopiëren en naar die locatie te verwijzen. Een andere mogelijkheid is om deze map rechtstreeks aan het Python Path toe te voegen in het doel [main_witmysql.wsgi] (dit is waarschijnlijk een betere oplossing);
  • regel 16: we kunnen Apache de installatiemap van Python in het bestandssysteem opgeven. Normaal gesproken bevindt deze zich in de PATH van de machine en is deze regel vaak overbodig (dat was hier het geval). Er kunnen echter meerdere Python-installaties op de machine staan en de gewenste installatie bevindt zich niet in de PATH van de machine. Dan lost deze regel het probleem op;

Op dezelfde manier maken we een bestand [flask-impots-withpgres.conf] aan:


# map van het .wsgi-script
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"

# naam van de website die door dit bestand wordt geconfigureerd
# hier zal deze flask-impots-withmysql heten
# de URL zullen van het type http(s)://flask-impots-withmysql/pad zijn
define SITE "flask-impots-withpgres"

# voeg het adres IP 127.0.0.1 toe voor de site SITE in c:/windows/system32/drivers/etc/hosts

# voeg hier de paden toe naar de te gebruiken Python-bibliotheken – scheid ze met komma’s
# hier de bibliotheken van een virtuele Python-omgeving
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"

# Python Home – alleen nodig als er meerdere versies van Python zijn geïnstalleerd
# WSGIPythonHome "C:/Program Files/Python38"

# URL HTTP
<VirtualHost *:80>
    # met de alias / zullen de URL de vorm /{prefixe_url}/action/... hebben
    # met de alias /impots zullen de URL de vorm /impots/{prefixe_url}/action/... aannemen
    # waarbij [prefixe_url] is gedefinieerd in parameters.py
    WSGIScriptAlias / "${ROOT}/main_withpgres.wsgi"
    DocumentRoot "${ROOT}"
    ServerName ${SITE}
    ServerAlias *.${SITE}
    <Directory "${ROOT}">
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

# URL beveiligd met HTTPS
<VirtualHost *:443>
    # met de alias / zullen de URL de vorm /{prefixe_url}/action/... hebben
    # met de alias /impots zullen de URL de vorm /impots/{prefixe_url}/action/... aannemen
    # waarbij [prefixe_url] is gedefinieerd in parameters.py
    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>

We slaan al deze bestanden op, starten de Apache-server en de bestanden SGBD, MySQL en PostgreSQL. De applicatie is geconfigureerd met de prefixen URL, [/do] en [with_csrftoken=False] (geen token CSRF) in [configs/parameters.py]. We vragen om URL en [https://flask-impots-withmysql/do]. Het antwoord van de server is als volgt:

Image

We vragen nu de URL en [https://flask-impots-pgres/do] op. Het antwoord is als volgt:

Image

Beide applicaties werken normaal.

Laten we nu de parameter [WSGIScriptAlias] wijzigen in [flask-impots-withmysql.conf]:


# map van het .wsgi-script
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"



# URL HTTP
<VirtualHost *:80>
    # met de alias / zullen de URL de vorm /{prefixe_url}/action/... aannemen
    # met de alias /impots zullen de URL de vorm /impots/{prefixe_url}/action/... aannemen
    # waarbij [prefixe_url] is gedefinieerd in parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …
</VirtualHost>

# URL beveiligd met HTTPS
<VirtualHost *:443>
    # met de alias / de URL krijgen de vorm /{prefixe_url}/action/...
    # met de alias /impots zullen de URL de vorm /impots/{prefixe_url}/action/... aannemen
    # waarbij [prefixe_url] is gedefinieerd in parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …

</VirtualHost>
  • regels 11 en 20: de alias WSI is nu [/impots];

We stoppen en starten de Apache-server opnieuw op en vragen vervolgens om URL en [https://flask-impots-withmysql/impots/do]. Het antwoord van de server is als volgt:

Image

Er is een crash opgetreden. De URL [1] geeft de oorzaak aan. Deze had [https://flask-impots-withmysql/impots/do/afficher-vue-authentification] moeten zijn. De alias WSGI ontbreekt. Dit is een fout in onze applicatie. De applicatie kan wel omgaan met een prefix van URL (/do is wel aanwezig). Je zou denken dat het toevoegen van het voorvoegsel [/impots/do] aan onze applicatie het voorgaande probleem zou oplossen. Maar nee. Dan stuiten we op andere soorten problemen. De alias WSGI gedraagt zich niet als een voorvoegsel van URL.

Laten we eens proberen te begrijpen wat er is gebeurd. We hebben URL aangevraagd in plaats van [https://flask-impots-withmysql/impots/do]. We verwachtten het authenticatiescherm te zien. Bij [1], hierboven, zien we dat de applicatie om de weergave ervan heeft gevraagd, maar niet met de juiste URL. Laten we het verloop van het verzoek [https://flask-impots-withmysql/impots/do] eens bekijken.

Allereerst werd de volgende route (configs/routes.py) uitgevoerd:


    # de routes van de Flask-applicatie
    # de root van de applicatie
    app.add_url_rule(f'{prefix_url}/', methods=['GET'],
                     view_func=routes.index)

De route op regel 3 is in ons voorbeeld [https://flask-impots-withmysql/impots/do]. We zien dat het gedeelte [https://flask-impots-withmysql/impots] uit de route is verwijderd, waardoor deze nu simpelweg [/do] is geworden. Voor het gedeelte [https://flask-impots-withmysql] is dit normaal; de servernaam wordt niet in de route opgenomen. Maar we zien dat ook de alias WSGI [/impots] niet wordt overgenomen. Dit is een belangrijk punt. Zelfs met een alias WSGI blijven onze oorspronkelijke routes geldig.

Laten we nu eens kijken wat de functie [index] op regel 4 (configs/routes_without_csrftoken) doet:


# de root van de applicatie
def index() -> tuple:
    # omleiding naar /init-session/html
    return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)

Op regel 4 worden we doorgestuurd naar de functie URL vanuit de functie [init_session]. In [configs/routes.py] is deze functie gekoppeld aan de route [/do/init-session/html]:


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

Regel 2: in onze test is [csrftoken_param] de lege tekenreeks. De applicatie ondersteunt hier geen token CSRF.

De functie [init_session] is als volgt gedefinieerd (configs/routes_without_csrftoken):


# init-session
def init_session(type_response: str) -> tuple:
    # de bij de actie behorende controller wordt uitgevoerd
    return front_controller()

Op regel 4 begint de verwerkingsketen van de actie [init-session]. Deze keten eindigt als volgt in [responses/HtmlResponse]:



# nu moet de omleidings-URL worden gegenereerd, zonder het token CSRF te vergeten indien dit wordt gevraagd
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

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

De actie [init-session] is een ADS-actie (Action Do Something) die eindigt met een omleiding naar een weergave, regel 9. Daar ligt het probleem. De functie [redirect] op regel 9 voegt niet automatisch de alias WSGI toe aan de omleidingsactie URL. Dat is te zien op de bovenstaande schermafbeelding. De alias /impots ontbreekt in het omleidingsobject URL.

De volgende versie biedt een oplossing voor het probleem met de alias WSGI.