<jupyter_start><jupyter_text>Programmation côté serveur - 3ème partie Dans cette dernière partie, nous abordons:- exploiter un **formulaire** avec la méthode `POST`; application à la réalisation d'une **session** très simple (nous réutiliserons les cookies).- **persister les données** côtés serveur; application à l'enregistrement de messages d'un utilisateur identifié.  Formulaire soumis avec la méthode `POST` La première méthode de soumission du formulaire modifie l'url et affiche en clair les valeurs envoyées ... cela n'est pas toujours souhaitable. Par exemple, imaginer le problème avec un formulaire d'authentification (login, mot de passe)... On peut donc soumettre le formulaire avec `method="post"` de façon à ce que les données envoyées soient placées dans le **corps** de la requête du navigateur (elles sont toutefois envoyées en clair si on n'utilise pas http**s**...)  Exemple Le programme cgi form_post.py (répertoire *demo_web_cgi/cgi-bin*) illustre la création d'une **session**.  On appelle **session** l'ensemble des requête/réponse http d'un utilisateur «reconnue» par le serveur. La **création d'une session** s'appuie sur l'**enregistrement d'un cookie** dont le rôle est d'identifier l'utilisateur en cours. Dans cet exemple volontairement très simple, si l'utilisateur connaît le mot de passe du site (trouver le!), il peut s'authentifier et accéder à un contenu réservé aux utilisateur connectés.  La logique de ce script est un peu plus élaborée que les précédents du fait que le programme peut s'exécuter dans plusieurs cas de figure:- **si** l'utilisateur n'a pas de cookie de session:     - **si** le formulaire n'a pas été soumis: on lui affiche le formulaire,    - **sinon si** (l'utilisateur vient de soumettre le formulaire) le mot de passe est correct:        - on demande l'enregistrement d'un cookie et on affiche le contenu «secret»,    - **sinon**: on réaffiche le formulaire avec un petit message d'erreur- **sinon** (l'utilisateur a un cookie de session):    - on récupère les informations de session (son login) en parsant le cookie,    - et on affiche le message secret.  Persistance des messages de l'utilisateur Nous souhaitons à présent qu'un utilisateur authentifié puisse écrire des messages, se déconnecter, revenir plus tard et **récupérer** ses messages. Pour parvenir à un tel résultat, il est nécessaire que le programme cgi enregistre les messages quelques part sur son hôte (la machine où il s'exécute).**N'oublier pas** que le programme cgi s'exécute à chaque requête du client, il ne peut donc mémoriser des information dans une variable pour long terme. On peut imaginer plusieurs façons de procéder pour enregistrer les informations, on peut le faire dans:1. un **simple fichier texte** comme un fichier *csv* ou *json* par exemple,2. un **fichier binaire** (non lisible) qui permet facilement de récupérer des données structurées (liste, dictionnaire, etc.),3.  une **base de données** (programme de terminale) - seule solution possible pour un site qui permet d'enregistre beaucoup d'informations de natures différentes.  Rappel python - les fichiers Pour ouvrir un fichier texte avec Python, on utilise la fonction prédéfinie `open("nom_fichier", mode)` laquelle renvoie un «handler» de fichier qui sert à agir dessus. Voilà à quoi cela ressemble basiquement, exemple d'un fichier ouvert en écriture (mode "w"):```python éviter de procéder comme cela; utiliser with (voir plus loin)f = open("mon_fichier", "w") f.write("...contenu...")...f.close()  libérer la ressource quand on a fini.``` Le **mode** précise si le fichier est ouvert en lecture `"r"`, en écriture `"w"`, en mode «ajout» `"a"` (on peut les combiner par ex `"rw"`)Par défaut, le fichier est ouvert en mode «texte». Pour un fichier **binaire**, on précise `b` au mode. Après ouverture, le fichier (qui est sur le disque) est placé en mémoire centrale dans une zone spéciale appelée tampon *buffer* (par le système d'exploitation); il est indispensable d'indiquer la fin de l'utilisation du fichier par `.close()` qui indique au système d'exploitation de libérer cette zone mémoire. Pour rendre ce processus plus net pour le programmeur, python dispose d'un mot clé spécial `with` (on appelle cela un manager de contexte). L'utilisation d'un fichier sera alors de la forme:```pythonwith open("mon_fichier", "w") as f:     bloc d'accès au fichier via f    f.write("...contenu...")    ...     à la sortie du bloc, le fichier est libéré: inutile de penser à close dans cette zone, le fichier a été libéré.``` Voici un exemple (attention, pour le tester, il faut copier ce notebook dans votre espace personnel):<jupyter_code>descripteurs = ["nom", "prenom", "email"]
valeurss = [
    ["Dupond", "tartampion", "<EMAIL>"],
    ["Ulrich", "Yahn", "<EMAIL>"],
]

with open("personnes_importantes", "w") as f:
    f.write(
        ",".join(descripteurs) + "\n"
    )
    for valeurs in valeurss:
        f.write(
            ",".join(valeurs) + "\n"
        )<jupyter_output><empty_output><jupyter_text>Après exécution de cette cellule, vous devriez apercevoir le fichier créé (sinon c'est que ce notebook n'est pas dans votre répertoire personnelle).Ajoutons une ligne, puis relisons le tout:<jupyter_code>with open("personnes_importantes", "a") as f:
    f.write("<NAME>, <EMAIL>\n")

with open("personnes_importantes", "r") as f:
    for ligne in f:
        print(ligne, end="")

print("fin de la démo")<jupyter_output><empty_output><jupyter_text>Observer ce qui se passe lorqque vous exécutez plusieurs fois la dernière cellule... comprenez-vous? Dans le contexte d'une application web, la difficulté avec cette manière de faire est qu'il est nécessaire de formater l'information dans le sens de l'écriture puis de décrypter ce format dans le sens de la lecture. Heureusement, Python propose des outils afin de sauvegarder/restaurer ses propres structures (liste, dictionnaire, ...)  Sauvegarder/Restaurer des données avec le module `pickle` Ce module, propre à Python, utilise un fichier binaire comme support de la sauvegarde.Voici comment il s'utilise dans les grandes lignes (sauvegarder -> `dump`; restaurer -> `load`):```pythonimport pickle sauvegarde d'une donnée structurée (qui peut être de type list, dict, set, ...)with open("sauvegarde", "wb") as f:     sauvegarde d'une structure référencée par variable    pickle.dump(variable, f) restaurationwith open("sauvegarde", "rb") as f:    variable = pickle.load(f)```  Exemple final Le programme cgi messages.py (répertoire *demo_web_cgi/cgi-bin*) permet à l'utilisateur authentifié d'écrire des messages sauvegardés par l'application.  Il utilise quelques éléments que nous n'avons pas encore vue: - **Redirection**: Pour éviter que le code soit trop long, on réutilise le programme        d'authentification *form_post.py* en utilisant une redirection à l'aide d'une balise d'entête HTML de   la forme:  ```html  <meta http-equiv="refresh"         content=";URL="  />  ```  Son effet est d'attendre le temps indiqué puis de demander la ressource précisée par URL. - **Gestion des cookie**: nous avons vu que décrypter un cookie n'était pas si simple. Heureusement le sous-module `cookies` de `http` permet de faire cela plus simplement:  ```python  import os  from http import cookies     récupération du cookie  cookie = cookies.SimpleCookie(      os.environ("HTTP_COOKIE")  )     extraction de la valeur associée à une clé  valeur_de_cle = cookie[""].value  ``` - **Tentative de lecture d'un fichier inexistant**: la première fois que la page d'écriture des messages est visitée, le fichier qui les contient n'existe pas.  Plutôt que de détecter si c'est la première fois que la page est visitée, on utilise un structure  permettant d'éviter que le programme ne s'arrête en cas d'erreur (tentative d'ouvrir un fichier non   existant). Voilà à quoi cette structure ressemble.  ```python  try:       code risquant de produire une erreur (on dit exception)  except:       code à appliquer si l'erreur se produit  ```  Vidéo d'aide<jupyter_code>%%HTML
<iframe src="https://player.vimeo.com/video/403634384" width="640" height="564" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09155101046992939, 'fraction_numerical': 0.0024348672997321647, 'mean_word_length': 4.708825573314802, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 22, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '215942', 'license': 'CC0-1.0', 'n_tokens_mistral': 2631, 'n_tokens_neox': 2646, 'n_words': 1096, 'path': '05_reseaux_web/2_le_web/5_prog_cote_serveur_3.ipynb', 'repo_name': 'efloti/cours-nsi-premiere', 'size': '8248'}