IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Introduction à Python 3 et au framework web Flask par l'exemple


précédentsommairesuivant

38. Exercice d’application : version 18

38-1. Implémentation

Image non disponible

Le dossier [impots/http-servers/13] est obtenu initialement par recopie du dossier [impots/http-servers/12] puis modifié partiellement.

Nous commençons par ajouter un nouveau paramètre dans le fichier [configs/parameters] :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
…        
        # token csrf
        "with_csrftoken"False,
        # bases gérées MySQL (mysql), PostgreSQL (pgres)
        "databases": ["mysql""pgres"],
        # préfixe des URL de l'application
        # mettre la chaîne vide si on ne veut pas de préfixe ou /préfixe sinon
        "prefix_url""/do",
        # url racine du serveur Apache - mettre la chaîne vide pour une exécution en-dehors d'Apache
        "application_root""/impots"

Ligne 10, le paramètre [application_root] représentera l’alias WSGI du serveur virtuel Apache.

Avec ce paramètre, nous pouvons corriger l’instruction de [responses/HtmlResponse] qui avait provoqué l’erreur :

Image non disponible
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
# maintenant il faut générer l'URL de redirection sans oublier le jeton CSRF s'il est demandé
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

        # réponse de redirection
        return redirect(f"{config['parameters']['application_root']}{config['parameters']['prefix_url']}{ads['to']}{csrf_token}")
, status.HTTP_302_FOUND
  • ligne 9 : nous avons ajouté la racine de l’application au début de l’URL cible de la redirection ;

Il nous faut également corriger tous les fragments pour que les URL qu’ils contiennent commencent par la racine de l’application (ou alias WSGI) :

Image non disponible

Le fragment [v-authentification]

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<!-- formulaire HTML - on poste ses valeurs avec l'action [authentifier-utilisateur] -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}"
>

    <!-- titre -->
    <div class="alert alert-primary" role="alert">
        <h4>Veuillez vous authentifier</h4>
    </div></form>

Le fragment [v-calcul-impot]

 
Sélectionnez
1.
2.
3.
4.
5.
6.
<!-- formulaire HTML posté -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
    <!-- message sur 12 colonnes sur fond bleu -->
    …

</form>

Le fragment [v-liste-simulations]

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
{% if modèle.simulations is defined and modèle.simulations|length!=0 %}<!-- tableau des simulations -->
<table class="table table-sm table-hover table-striped">
    …
    <tr>
        <th scope="row">{{simulation.id}}</th>
        <td>{{simulation.marié}}</td>
        <td>{{simulation.enfants}}</td>
        <td>{{simulation.salaire}}</td>
        <td>{{simulation.impôt}}</td>
        <td>{{simulation.surcôte}}</td>
        <td>{{simulation.décôte}}</td>
        <td>{{simulation.réduction}}</td>
        <td>{{simulation.taux}}</td>
        <td><a href="{{modèle.application_root}}{{modèle.prefix_url}}/supprimer-simulation/{{simulation.id}}{{modèle.csrf_token}}">Supprimer</a></td>
    </tr>
    {% endfor %}
    </tr>
    </tbody>
</table>
{% endif %}

Le fragment [v-menu]

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
<!-- menu Bootstrap -->
<nav class="nav flex-column">
    <!-- affichage d'une liste de liens HTML -->
    {% for optionMenu in modèle.optionsMenu %}
    <a class="nav-link" href="{{modèle.application_root}}{{modèle.prefix_url}}{{optionMenu.url}}{{modèle.csrf_token}}">{{optionMenu.text}}</a>
    {% endfor %}
</nav>

Les fragments ci-dessus utilisent tous le modèle [modèle.application_root]. Pour l’instant la clé [application_root] n’existe pas dans les modèles générés par les classes de modèles.

Image non disponible

La classe [AbstractBaseModelForView] qui est la classe parent de toutes les classes générant un modèle devient la suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
from abc import abstractmethod

from flask import Request
from flask_wtf.csrf import generate_csrf
from werkzeug.local import LocalProxy

from InterfaceModelForView import InterfaceModelForView

class AbstractBaseModelForView(InterfaceModelForView):

    @abstractmethod
    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict-> dict:
        pass

    def update_model(self, modèle: dict, config: dict):
        # calcul du jeton CSRF
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""
        # mise à jour du modèle passé en paramètre
        modèle.update({
            # jeton csrf
            'csrf_token': csrf_token,
            # prefix_url
            'prefix_url': config["parameters"]["prefix_url"],
            # application_root
            'application_root': config["parameters"]["application_root"],
        })
  • ligne 15 : la méthode [update_model] a pour rôle de mettre dans le modèle des vues :

    • ligne 24 : le jeton CSRF ;

    • ligne 26 : le préfixe des URL ;

    • ligne 28 : la racine de l’application ou alias WSGI ;

Les quatre classes filles font appel à la classe parent avec le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
…
        # actions possibles à partir de la vue
        modèle['actions_possibles'= ["afficher-vue-authentification""authentifier-utilisateur"]

        # finition du modèle par la classe parent
        super().update_model(modèle, config)

        # on rend le modèle
        return modèle
  • ligne 6 : chaque classe fille appelle sa classe parent pour mettre à jour le modèle qu’elle a créé ;

La version 18 est prête. Nous reprenons les deux serveurs virtuels d’Apache de la version 17 et nous les modifions :

Image non disponible

Les deux fichiers [flask-impots-withXX.conf] ne sont modifiés qu’en un seul point :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
# dossier du script .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/13/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/chemin
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 /impots "${ROOT}/main_withmysql.wsgi"
    …
</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 /impots "${ROOT}/main_withmysql.wsgi"
    ..

</VirtualHost>
  • ligne 12, on utilise maintenant le dossier [impots/http-servers/13/apache].

Nous sommes prêts pour les tests de la version 18 portée sur Apache. La configuration est la suivante :

  • l’alias WSGI est /impots dans les deux fichiers de configuration des serveurs virtuels ;

  • dans le fichier de paramétrage [configs/parameters], les paramètres sont les suivants :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
        # token csrf
        "with_csrftoken"False,
        # préfixe des URL de l'application
        # mettre la chaîne vide si on ne veut pas de préfixe ou /préfixe sinon
        "prefix_url""/do",
        # url racine du serveur Apache - mettre la chaîne vide pour une exécution en-dehors d'Apache
        "application_root""/impots"

On lance le serveur Apache ainsi que les deux SGBD. On demande l’URL [https://flask-impots-withmysql/impots/do]. La réponse du serveur est la suivante :

Image non disponible

On obtient bien la vue d’authentification qu’on n’avait pu obtenir dans la version précédente. Le reste de l’application fonctionne normalement.

Maintenant on teste l’autre serveur virtuel. On demande l’URL [https://flask-impots-withpgres/impots/do]. La réponse du serveur est la suivante :

Image non disponible

Les notions d’alias WSGI et de préfixe d’URL jouent le même rôle. L’une de ces deux notions est redondante. Ainsi pour préfixer les URL du serveur Apache avec la chaîne [/impots/do], on peut s’y prendre de trois manières :

  1. [WGSIAlias /impots] et [prefix_url=’/do’] ;
  2. [WGSIAlias /] et [prefix_url=’/impots/do’] ;
  3. [WGSIAlias /impots/do] et [prefix_url=’’] ;

38-2. Tests console

On utilise de nouveau les tests console du client [http-clients/09] :

Image non disponible
  • l’URL du serveur doit être modifiée dans les configurations [1] et [3] ;

  • il faut apporter une modification à la couche [dao] pour qu’elle supporte le protocole HTTPS du serveur Apache ;

Dans les fichiers [config], l’URL du serveur devient la suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
        "server": {
            # "urlServer": "http://127.0.0.1:5000",
            # "urlServer": "http://127.0.0.1:5000/do",
            "urlServer""https://flask-impots-withmysql/impots/do",
            "user": {
                "login""admin",
                "password""admin"
            },
            "url_services": {
                …
            }
        },
        # mode debug
        "debug"True,
        # csrf_token
        "with_csrftoken"False,
  • ligne 4 : la nouvelle URL du serveur. Pour la 1ère fois dans ce document, le client utilise le protocole HTTPS ;

La classe [ImpôtsDaoWihHttpSession] de la couche [dao] évolue de la façon suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
…    
    # étape request / response
    def get_response(self, method: str, url_service: str, data_value: dict = None, json_value=None):
        # [method] : méthode HTTP GET ou POST
        # [url_service] : URL de service
        # [data] : paramètres du POST en x-www-form-urlencoded
        # [json] : paramètres du POST en json
        # [cookies]: cookies à inclure dans la requête

        # on doit avoir une session XML ou JSON, sinon on ne pourra pas gérer la réponse
        if self.__session_type not in ['json''xml']:
            raise ImpôtsError(73"il n'y a pas de session valide en cours")

        # on ajoute le jeton CSRF à l'URL de service
        if self.__csrf_token:
            url_service = f"{url_service}/{self.__csrf_token}"

        # exécution de la requête
        response = requests.request(method,
                                    url_service,
                                    data=data_value,
                                    json=json_value,
                                    cookies=self.__cookies,
                                    allow_redirects=True,
                                    # pour le protocole https
                                    verify=False)

        # mode debug ?
        if self.__debug:
            # logueur
            if not self.__logger:
                self.__logger = self.__config['logger']
            # on logue
            self.__logger.write(f"{response.text}\n")

        …

        # on rend le résultat
        return résultat['réponse']
  • ligne 26, on ajoute le paramètre [verify=False] à cause du protocole HTTPS utilisé par le serveur Apache. Le module [requests] (ligne 19) sait gérer nativement le protocole HTTPS. Par défaut, il vérifie la validité du certificat de sécurité que lui envoie le serveur HTTPS et lance une exception si le certificat reçu n’est pas valide. C’est le cas ici où le serveur Apache de Laragon envoie un certificat auto-signé. Pour éviter l’exception, on utilise le paramètre [verify=False] pour dire au module [requests] de ne pas lancer d’exception. [requests] se contente alors d’afficher un avertissement (warning) sur la console.

Ces modifications faites, tous les tests console doivent fonctionner.


précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Serge Tahé et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2020 Developpez.com.