37. Practical Exercise: Version 17

This new version introduces the following changes:
- it will be ported to an Apache/Windows server;
- To perform this port, version 17 contains all the dependencies it needs in its [impots/http-servers/12] folder. Note that previous versions retrieved their dependencies from various folders throughout the entire [python-flask-2020] project;
37.1. Relocation of application dependencies

Note that application dependency management is handled in the [syspath] script. In the previous version, this script was as follows:
We need to relocate all dependencies whose absolute path depends on the [root_dir] variable in line 8, i.e., lines 13–26.
The [syspath] script for the new version will be as follows:
- lines 8–27: all dependencies are now relative to the [script_dir] variable on line 5;
- lines 42–45: the [root_dir] variable has been removed from the syspath configuration;
- line 10: the application entities are in the [entities] folder [1];
- line 12: the [dao] layer is in the [layers/dao] folder [2];
- line 14: the [business] layer is in the [layers/business] folder [2];
- line 16: the [Logger, SendMail] utilities are in the [utilities] folder [3];
- lines 29–40: the application’s Python Path is calculated without importing the [myutils] module;

37.2. Tests
At this point, version 17 should work. Verify it.
37.3. Porting a Python/Flask application to an Apache/Windows server
37.3.1. Sources
To port a Flask application to Apache/Windows, I had to search the Internet. Here is the link that helped me get started: [https://medium.com/@madumalt/flask-app-deployment-in-windows-apache-server-mod-wsgi-82e1cfeeb2ed];
I used the information from this link except for the Apache server configuration. For that, I used a sample Apache server configuration from Laragon.
37.3.2. Installing the Python mod_wsgi module
The Python/Flask application we developed used the WSGI (Web Server Gateway Interface) server [werkzeug] included with Flask. This server is described |here|. The link [https://www.fullstackpython.com/wsgi-servers.html] describes how WSGI servers work. There are various |WSGI servers|. One of these is the Apache server running in WSGI mode. This is the solution adopted here because Laragon, which we installed, comes with an Apache server.
In order for the Apache server to host a Python application, we need to install the Python module [mod_wsgi]. Installing this module is tricky because it involves a C++ compilation. To complete the installation successfully, you need a Microsoft C++ compiler. A simple solution is to install the latest version of Visual Studio Community [https://visualstudio.microsoft.com/fr/vs/community/].
If you don’t need Visual Studio for anything other than [mod_wsgi], you can limit the installation to the C++ environment:

Once the C++ compiler is installed, the [mod_wsgi] module is installed in a PyCharm terminal:
(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
- Line 1: We set the value of the [MOD_WSGI_APACHE_ROOTDIR] environment variable. This value is the location of the Apache server in the file system. Here, this location is [<laragon>\bin\apache\httpd-2.4.35-win64-VC15], where <laragon> is the Laragon installation folder. You can obtain this location in various ways. Here is one obtained using one of Laragon’s options:

In [1-3], the [httpd.conf] file is the main configuration file for the Apache server. The file in question is then opened in a text editor (Notepad++ below):

In [2], the Apache installation folder is the part preceding the string [conf\httpd.conf].
Let’s return to the installation of the [mod_wsgi] module:
- Lines 3–9: installation of the [mod_wsgi] module;
37.3.3. Configuring the Laragon Apache Server
We will configure the Laragon Apache server. We start with its main configuration file [httpd.conf]:

We go to the end of the [httpd.conf] file:
…
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"
- Line 8 has been added to the existing [httpd.conf] file at the end of the file. It tells the Apache server where to find a component of the [mod_wsgi] module that we just installed;
An easy way to get the path for line 8 is to run the following command in a PyCharm terminal:
(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"
Some documentation states that lines 2 and 3 should be added to the end of the [httpd.conf] file. In my case, line 3 above caused an error (missing [encodings] module). Therefore, it was not included in the [httpd.conf] file. Only line 2 was added. The meanings of the various [mod_wsgi] module parameters that can be used in Apache configuration files are described |here|.
Next, we will enable the HTTPS protocol on the Apache server:

- In [1-4], we enable the HTTPS protocol on the Apache server;
From now on, we will be able to use [https://serveur/chemin] URLs;
To configure Apache to serve a Flask application, the link referenced |above|uses virtual hosts. Laragon also offers virtual host management:

- In [1-3], we ask Laragon to automatically create virtual hosts;
The next step is to create a web project with Laragon:
![]() |
![]() | ![]() |
- In [1-3], create an empty PHP project;
- In [4-8], Laragon has created a virtual site named [auto.projet-test.test] configured by the file [auto.projet-test.test.conf] [8] in the [sites-enabled] folder [7]. This folder is located at [<laragon>\etc\apache2\sites-enabled], where [laragon] is Laragon’s installation folder;
Although this is not part of what we are doing right now, you might be curious to check out the [test-project] website we just created:

- In [1-5], an empty project was created. It is a PHP project located in the [<laragon>/www] folder, where [laragon] is the Laragon installation folder;
Now let’s examine the [auto.projet-test.test.conf] file generated by Laragon in the [<laragon>\etc\apache2\sites-enabled] folder:
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>
- line 1: the root directory of the created project in the file system;
- line 2: the name of the virtual server. URLs for this server will be in the form [http(s)://test-project.test/path];
- lines 4–12: configuration of the virtual site for port 80 (line 4) and the HTTP protocol;
- lines 14–27: configuration of the virtual host for port 443 (line 14) and the HTTPS protocol;
Let’s see how a virtual server works. First, let’s start the Apache and PHP servers:

Then, using a browser, we request the URL [http://projet-test.test/]:

- in [1], the requested URL;
- in [2], the HTTP protocol was used;
- in [3], because the [projet-test] project is empty, we get the index of its folder (list of its contents), an empty index;
Now let’s request the secure URL [https://projet-test.test/]:

- In [1-2], we get the same response as before, but using the HTTPS protocol [1];
Creating the virtual server [test-project.test] created a new entry in the file [<windows>/system32/drivers/etc/hosts]:

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
127.0.0.1 projet-test.test #laragon magic!
- Line 23: The IP address for the name [test-project.test] is 127.0.0.1, i.e., the address of [localhost] (line 20), the local machine. So when you type the URL [http://projet-test.test/chemin] into a browser, the request is sent to the address 127.0.0.1 on port 80. The Apache server on the local machine (localhost) then responds.
You might wonder why, when you type the request [http://projet-test.test/], the Apache server uses the configuration from the file [<laragon>\etc\apache2\sites-enabled\auto.projet-test.test.conf]:

To understand this, we need to see what the browser sends to the Apache server when making this request. Let’s do this with Postman:

- In [1-3], we send an HTTPS request [1];
- in [4], Postman indicates that it did not recognize the security certificate. The HTTPS protocol establishes an encrypted connection between the web client (here, Postman) and the Apache server. This encrypted connection is established through certificates exchanged between the client and the server. It is the server that initiates the process of establishing the encrypted connection by sending a security certificate to the client. For this certificate to be accepted by the client, it must be signed—in other words, purchased from companies authorized to issue security certificates. When we enabled Laragon’s HTTPS protocol, Laragon created the security certificate itself. The certificate is then said to be self-signed. Most web clients issue a warning when they receive a self-signed certificate. This is what Postman does in [4]. Most web clients then offer to disable verification of the security certificate sent by the server. This is what Postman offers in [5];
We click on the link [5] to disable SSL (Secure Sockets Layer) verification. SSL/TLS (Transport Layer Security) is a security protocol that creates a secure communication channel between two machines on the internet. This is the protocol used here by Apache. The response is as follows:

We receive the same page as with a traditional browser. Now let’s look at the client/server dialogue in the 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
- Line 6: The HTTP [Host] header specifies the name of the server targeted by the web client. This is the principle behind virtual servers. At a single IP address (here 127.0.0.1), a web server can host multiple websites with different names. The HTTP [Host] header allows the client to specify which server (here, the one at address 127.0.0.1) it is addressing;
So what does Apache do?
When it starts up, Apache reads all configuration files found in the [[<laragon>\etc\apache2\sites-enabled]] directory:

Each configuration file defines a virtual server. For example, in the file [auto.projet-test.test.conf], you will find the following line:
define ROOT "C:/MyPrograms/laragon/www/projet-test/"
define SITE "projet-test.test"
…
Line 2 defines the [test-project.test] virtual server. The [auto.projet-test.test.conf] file is the configuration for this virtual server. Because it reads all configuration files in the [<laragon>\etc\apache2\sites-enabled] folder at startup, the Apache server knows that a virtual server named [projet-test.test] exists. Thus, when it receives the following HTTPS request from the Postman client:
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
It recognizes that the request is addressed to the virtual server [test-project.test] (line 6) and that it exists. It then uses the configuration of the virtual server [test-project.test] to respond to the Postman client.
37.4. Creating Your First Apache Virtual Server
Now that we know what virtual servers are used for and how to configure them, we’ll create one. It will be used to run a Python Flask application installed in an [Apache] folder of version 17 currently being deployed on the Apache server:
We have placed the application developed in the section |link|—a date/time web service—in the [http-servers/12/apache/example] folder:

The [date_time_server.py] server is as follows:
The Flask application is referenced by the identifier [application] (lines 14, 43, 44). This name is required. If you reference the Flask application with a different identifier, the application will not work and will display an error message indicating that it cannot find the requested URL. This error message provides no indication of the source of the error. You must therefore be careful about this.
The HTML file referenced on line 34 is as follows:
<!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] will be the virtual server hosting this application. It will be configured via the file [<laragon>\etc\apache2\sites-enabled\date-time-server.conf] (note that this name is arbitrary—Apache reads all files in [sites-enabled] to discover the hosted virtual sites);
We first obtain this file by copying the [auto.projet-test.test.conf] file and then modify it.

The [date-time-server.conf] file will look like this:
- Line 7: Give a name to the virtual server configured by the file;
- line 2: sets the value of the [ROOT] variable used on lines 14 and 27;
- Lines 14 and 27: Specify the path to the Python script that must be executed when the virtual server receives a request. Here, we specify that requests for the [date-time-server] server are handled by the Python script [date_time_server.py]. This difference from the [auto.projet-test.test.conf] file stems from the fact that the former configured a PHP server, whereas the [date-time-server.conf] file configures a Python server;
- Lines 14 and 27: The [WSGIScriptAlias /] attribute specifies here that the root of the [date-time-server] server will be [/]. Thus, the application’s URLs will take the form [http(s)://date-time-server/path];
- Lines 14 and 27: We can assign a different root to the application, for example [WSGIScriptAlias /show]. In this case, the application’s URLs will take the form [http(s)://show/date-time-server/path];
We also need to add a line to the [<windows>/system32/drivers/etc/hosts] file:
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
127.0.0.1 flask-impots-withmysql # cours python - appli impôts avec MySQL
127.0.0.1 flask-impots-withpgres # cours python - appli impôts avec PostgreSQL
127.0.0.1 date-time-server # cours python - heure et date du moment
127.0.0.1 projet-test.test #laragon magic!
We add line 25 to assign the IP address [127.0.0.1] to the virtual server [date-time-server].
Let’s check everything. We start the Apache server:

Next, we request the URL [https://date-time-server] using a browser:
- in [1], the requested URL;
- in [3], the server’s response;
- in [2], the browser indicates that the HTTPS connection is not secure because it detected that the certificate sent by the Apache server was self-signed;
Now, in the [date-time-server.conf] file, let’s add an alias to lines 14 and 27:
WSGIScriptAlias /show-date-time "${ROOT}/date_time_server.py"
The change is not immediately recognized by the Apache server. You must reload it:

We then request the URL [https://date-time-server/show-date-time]. The server’s response is as follows:

37.5. Porting the tax calculation application to Apache / Windows
The [apache] directory [2] is initially created by copying the [main] directory. It is important that they are at the same level so that the paths in the [syspath.py] script copied from [1] to [2] remain valid. To avoid interfering with the working [impots / http-servers/ 12] application, we place the configuration to be executed by the Apache server in [apache];

- the [config] file in [2] is the same as the [config] file in [1];
- the [syspath] file in [2] is the same as the [syspath] file in [1];
- the [main_withmysql] file in [2] is the [main] file from [1] with the following modifications:
The main script [main] received a [mysql / pgres] parameter that told it which DBMS to use. The [main_withmysql] script uses the MySQL DBMS:
Line 7 sets the DBMS to MySQL.
- The [main_withpgres] file from [2] is the [main] file from [1] with the following changes: it uses the PostgreSQL DBMS:
On line 7, we set the DBMS to PostgreSQL.
Once this is done, we create the following script [main_withmysql.wsgi] (the suffix used does not matter):
The script [main_withmysql.wsgi] will be the target executed by the Apache server in WSGI mode:
- The Apache server’s target could have been the script [main_withmysql.py], as was done previously with the script [date_time_server.py]. But it would have required a slight modification:
- unlike when running a console script, with Apache, the directory containing the target [main_withmysql.py] is not part of the Python Path. Therefore, line 6 of the [main_withmysql.py] script causes an error;
- the second change that would have been necessary is that in [main_withmysql], the Flask application is referenced by the identifier [app]. We know that for Apache/WSGI, it must also be referenced by an identifier [application];
- Instead of modifying [main_withmysql.py], we change the Apache target. It will now be the [main_withmysql.wsgi] script above:
- lines 1–7: we add the script’s directory to the Python Path. As a result, line 6 of [main_withmysql.py] no longer causes an error;
- lines 9–10: importing [main_withmysql.py] triggers its execution. Additionally, we reference the Flask application [app] found in [main_withmysql.py] using the [application] identifier required by Apache in WSGI mode;
We do the same with the [main_withpgres.wsgi] script:
We now have the executable targets for the Apache server. We now need to create two virtual servers, one for each target.
In [<laragon>\etc\apache2\sites-enabled], we create the file [flask-impots-withmysql.conf] (the name doesn’t matter):

# dossier du script .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"
# nom du site web configuré par ce fichier
# ici il s'appellera flask-impots-withmysql
# les URL seront du type http(s)://flask-impots-withmysql/path
define SITE "flask-impots-withmysql"
# mettre l'adresse IP 127.0.0.1 pour site SITE dans c:/windows/system32/drivers/etc/hosts
# mettre ici les chemins des bibliothèques Python à utiliser - les séparer par des virgules
# ici les bibliothèques d'un environnement virtuel Python
WSGIPythonPath "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"
# Python Home - nécessaire uniquement s'il y a plusieurs versions de Python installées
# WSGIPythonHome "C:/Program Files/Python38"
# URL HTTP
<VirtualHost *:80>
# avec l'alias / les URL auront la forme /{prefixe_url}/action/...
# avec l'alias /impots les URL auront la forme /impots/{prefixe_url}/action/...
# où [prefixe_url] est défini dans parameters.py
WSGIScriptAlias / "${ROOT}/main_withmysql.wsgi"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# URL sécurisées avec HTTPS
<VirtualHost *:443>
# avec l'alias / les URL auront la forme /{prefixe_url}/action/...
# avec l'alias /impots les URL auront la forme /impots/{prefixe_url}/action/...
# où [prefixe_url] est défini dans 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>
- line 2: the application root, the [apache] folder we created;
- lines 23, 38: the target [main_withmysql.wsgi] that we created:
- line 7: the virtual server will be named [flask-impots-withmysql];
- line 13: the [WSGIPythonPath] directive allows us to add folders to the Python Path. Here, Apache is unaware that we used a virtual environment to develop the application and that all modules used by the application are in that virtual environment. Therefore, on line 13, we add the folder containing all modules from the virtual environment used. One option is to copy this directory to another location in the file system and reference that location. Another option is to add this directory to the Python Path directly within the [main_withmysql.wsgi] target (this is likely a better solution);
- line 16: we can specify the Python installation directory in the file system to Apache. Normally, this is in the machine’s PATH, and often this line is unnecessary (which was the case here). However, there may be multiple Python installations on the machine, and the desired one may not be in the machine’s PATH. In that case, this line resolves the issue;
Similarly, create a [flask-impots-withpgres.conf] file:
# dossier du script .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"
# nom du site web configuré par ce fichier
# ici il s'appellera flask-impots-withmysql
# les URL seront du type http(s)://flask-impots-withmysql/path
define SITE "flask-impots-withpgres"
# mettre l'adresse IP 127.0.0.1 pour site SITE dans c:/windows/system32/drivers/etc/hosts
# mettre ici les chemins des bibliothèques Python à utiliser - les séparer par des virgules
# ici les bibliothèques d'un environnement virtuel Python
WSGIPythonPath "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"
# Python Home - nécessaire uniquement s'il y a plusieurs versions de Python installées
# WSGIPythonHome "C:/Program Files/Python38"
# URL HTTP
<VirtualHost *:80>
# avec l'alias / les URL auront la forme /{prefixe_url}/action/...
# avec l'alias /impots les URL auront la forme /impots/{prefixe_url}/action/...
# où [prefixe_url] est défini dans parameters.py
WSGIScriptAlias / "${ROOT}/main_withpgres.wsgi"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# URL sécurisées avec HTTPS
<VirtualHost *:443>
# avec l'alias / les URL auront la forme /{prefixe_url}/action/...
# avec l'alias /impots les URL auront la forme /impots/{prefixe_url}/action/...
# où [prefixe_url] est défini dans 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 save all these files, start the Apache server and the MySQL and PostgreSQL databases. The application is configured with the URL prefix [/do] and [with_csrftoken=False] (no CSRF token) in [configs/parameters.py]. We request the URL [https://flask-impots-withmysql/do]. The server’s response is as follows:

We now request the URL [https://flask-impots-pgres/do]. The response is as follows:

Both applications are working normally.
Now let’s modify the [WSGIScriptAlias] parameter in [flask-impots-withmysql.conf]:
- lines 11 and 20, the WSI alias is now [/impots];
We stop and restart the Apache server, then request the URL [https://flask-impots-withmysql/impots/do]. The server’s response is as follows:

There is a crash. The URL [1] tells us the cause. It should have been [https://flask-impots-withmysql/impots/do/afficher-vue-authentification]. The WSGI alias is missing. This is an error in our application. It knows how to handle a URL prefix (/do is present). One might think that adding the prefix [/impots/do] to our application would solve the previous problem. But no. We then encounter other types of problems. The WSGI alias does not behave like a URL prefix.
Let’s try to understand what happened. We requested the URL [https://flask-impots-withmysql/impots/do]. We expected to see the authentication view. In [1] above, we see that the application requested to display it, but not with the correct URL. Let’s examine the path of the request [https://flask-impots-withmysql/impots/do].
First, the following route (configs/routes.py) was executed:
# les routes de l'application Flask
# racine de l'application
app.add_url_rule(f'{prefix_url}/', methods=['GET'],
view_func=routes.index)
The route on line 3 is [https://flask-impots-withmysql/impots/do] in our example. We can see that the route has been stripped of the [https://flask-impots-withmysql/impots] part to become simply [/do]. As for the [https://flask-impots-withmysql] part, this is normal; the server name is not included in the route. But we can see that it also does not include the WSGI alias [/impots]. This is an important point. Even with a WSGI alias, our initial routes remain valid.
Now let’s see what the [index] function on line 4 (configs/routes_without_csrftoken) does:
On line 4, we are redirected to the URL of the [init_session] function. In [configs/routes.py], this function has been associated with the 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)
Line 2: In our test, [csrftoken_param] is the empty string. The application does not handle a CSRF token here.
The [init_session] function is defined as follows (configs/routes_without_csrftoken):
Line 4: We start the action processing chain [init-session]. This chain ends as follows in [responses/HtmlResponse]:
…
# now it's time to generate the URL redirection, not forgetting the CSRF token if requested
if config['parameters']['with_csrftoken']:
csrf_token = f"/{generate_csrf()}"
else:
csrf_token = ""
# redirect response
return redirect(f"{config['parameters']['prefix_url']}{ads['to']}{csrf_token}"), status.HTTP_302_FOUND
The [init-session] action is an ADS (Action Do Something) action that ends with a redirect to a view, line 9. That is where the problem lies. The [redirect] function on line 9 does not automatically add the WSGI alias to the redirect URL. This is shown in the screenshot above. The /impots alias is missing from the redirect URL.
The following version provides a solution to the WSGI alias problem.


