Programmation CGI en Python
Scripting web hérité avec Python (Comprendre les concepts CGI)
Section titled “Scripting web hérité avec Python (Comprendre les concepts CGI)”Remarque : Ce tutoriel aborde les concepts du Common Gateway Interface (CGI) en utilisant Python. Bien que le CGI ait été historiquement important, il n’est plus la norme pour le développement web Python moderne. Des frameworks comme Flask, Django et FastAPI, utilisant les interfaces WSGI ou ASGI, sont l’approche recommandée aujourd’hui. Comprendre les concepts du CGI peut fournir un contexte historique utile et un aperçu de la manière dont les serveurs web interagissent avec les scripts backend.
Le Common Gateway Interface (CGI) est un protocole standard qui définit comment les serveurs web peuvent exécuter des scripts externes (comme les programmes Python) pour générer dynamiquement du contenu web. Il spécifie comment les informations sont échangées entre le serveur et le script.
Qu’était le CGI ?
Section titled “Qu’était le CGI ?”- Le CGI offrait un moyen standard pour les serveurs web (comme Apache ou Nginx) d’exécuter des programmes externes (scripts CGI) en réponse aux requêtes web entrantes.
- Au lieu de simplement servir des fichiers statiques, le serveur pouvait exécuter un script, et la sortie de ce script était renvoyée au navigateur de l’utilisateur.
Flux de Requête Web Basique (Conceptuel)
Section titled “Flux de Requête Web Basique (Conceptuel)”Comprendre l’interaction de base aide à saisir le rôle joué par le CGI :
- Le navigateur d’un utilisateur demande une URL spécifique à un serveur web.
- Le serveur web identifie si l’URL pointe vers un fichier statique ou vers un script configuré pour être exécuté via une interface comme le CGI.
- S’il s’agit d’un script, le serveur l’exécute, transmettant les détails de la requête (comme les données de formulaire ou les paramètres de requête) généralement via des variables d’environnement et l’entrée standard.
- Le script traite la requête, effectue des actions (par exemple, des requêtes de base de données) et génère une réponse HTTP (y compris des en-têtes comme
Content-Typeet le corps HTML). - Le script affiche cette réponse sur la sortie standard.
- Le serveur web capture la sortie du script et la renvoie au navigateur.
- Le navigateur affiche le contenu reçu.
Dans le modèle CGI, chaque requête lançait typiquement un nouveau processus pour le script, ce qui était inefficace. Les approches modernes (WSGI/ASGI) utilisent des serveurs d’applications persistants pour de meilleures performances.
Architecture CGI (Diagramme Conceptuel)
Section titled “Architecture CGI (Diagramme Conceptuel)”Imaginez ce flux :
- Le navigateur envoie la requête (par exemple, pour
/scripts/my_script.py) -> - Le serveur web reçoit la requête ->
- Le serveur web identifie qu’il doit exécuter
my_script.pyvia CGI -> - Le serveur web démarre
my_script.py, transmettant les données de la requête -> my_script.pys’exécute, génère une sortie HTML ->my_script.pyaffiche le HTML sur la sortie standard ->- Le serveur web capture la sortie ->
- Le serveur web renvoie la sortie au navigateur ->
- Le navigateur affiche le HTML.
Configuration du serveur web (Contexte historique)
Section titled “Configuration du serveur web (Contexte historique)”Traditionnellement, les serveurs web nécessitaient une configuration spécifique pour activer l’exécution CGI, désignant souvent des répertoires spécifiques (comme cgi-bin) où les scripts exécutables pouvaient être placés. Les permissions devaient également être définies correctement.
Les frameworks web Python modernes s’exécutent typiquement via des serveurs WSGI ou ASGI (comme Gunicorn, uWSGI, Hypercorn, Uvicorn), qui sont configurés différemment et gèrent le cycle de vie de l’application plus efficacement, souvent derrière un proxy inverse comme Nginx.
Exemple de script simple (Illustrant la sortie)
Section titled “Exemple de script simple (Illustrant la sortie)”Cet exemple montre le type de sortie qu’un script CGI devait produire. Notez l’en-tête Content-Type essentiel suivi d’une ligne vide, puis du corps HTML. Les frameworks modernes gèrent la génération de réponses plus élégamment.
#!/usr/bin/env python3# -*- coding: utf-8 -*-
# Affiche l'en-tête HTTP obligatoire# Notez la ligne vide séparant l'en-tête du corpsprint("Content-Type: text/html\n")
# Affiche le contenu HTMLprint("<!DOCTYPE html>")print("<html>")print("<head>")print("<title>Bonjour depuis le script Python</title>")print("</head>")print("<body>")print("<h2>Bonjour le monde ! Ceci est la sortie du script.</h2>")print("</body>")print("</html>")S’il était exécuté par un serveur web configuré pour le CGI, ce script enverrait une page HTML de base au navigateur.
En-têtes HTTP
Section titled “En-têtes HTTP”La ligne Content-Type: text/html est un en-tête HTTP crucial. Elle indique au navigateur le type de contenu à attendre. De nombreux autres en-têtes existent pour contrôler la mise en cache, la redirection, les cookies, etc. Les scripts CGI devaient afficher ces en-têtes manuellement avant le contenu réel.
En-têtes HTTP courants (Conceptuel) :
| En-tête | Description |
|---|---|
| Content-Type | Spécifie le type MIME du corps de la réponse (par exemple, text/html, application/json, image/png). Essentiel. |
| Location | Utilisé pour la redirection. Indique au navigateur de demander une URL différente. |
| Set-Cookie | Envoie un cookie depuis le serveur pour être stocké par le navigateur. |
| Cache-Control | Fournit des directives pour les mécanismes de mise en cache (par exemple, no-cache). |
Les frameworks modernes fournissent des fonctions d’aide ou des objets pour définir les en-têtes de réponse sans impression manuelle.
Variables d’environnement CGI (Conceptuel)
Section titled “Variables d’environnement CGI (Conceptuel)”Le serveur web transmettait les informations de requête au script CGI via des variables d’environnement. Bien que l’accès direct soit rare aujourd’hui, comprendre ces variables montre quelles données sont disponibles pendant une requête.
| Nom de la variable (Conceptuel) | Description |
|---|---|
| REQUEST_METHOD | La méthode HTTP utilisée (par exemple, ‘GET’, ‘POST’). |
| QUERY_STRING | La partie de l’URL après ’?’ contenant les paramètres GET. |
| CONTENT_TYPE | Le type MIME du corps de la requête (important pour les requêtes POST, par exemple, application/x-www-form-urlencoded). |
| CONTENT_LENGTH | La longueur du corps de la requête en octets (important pour les requêtes POST). |
| SCRIPT_NAME | Le chemin du script en cours d’exécution. |
| REMOTE_ADDR | L’adresse IP du client effectuant la requête. |
| HTTP_USER_AGENT | La chaîne d’identification du navigateur/client. |
| HTTP_COOKIE | Cookies envoyés par le client. |
| SERVER_NAME | Le nom d’hôte ou l’adresse IP du serveur. |
| SERVER_PORT | Le port sur lequel le serveur a reçu la requête. |
Les frameworks modernes fournissent ces informations via des objets de requête dédiés (par exemple, request.method, request.args, request.headers, request.cookies dans Flask ou Django).
Exemple : Accès aux informations d’environnement (Conceptuel)
#!/usr/bin/env python3import os
print("Content-Type: text/html\n")print("<!DOCTYPE html><html><head><title>Infos Environnement</title></head><body>")print("<h1>Environnement de la requête (Conceptuel)</h1>")print("<pre>")# Dans un contexte CGI réel, ces variables seraient remplies par le serveur.# Dans les frameworks modernes, l'accès se fait via un objet de requête.for param, value in os.environ.items(): # Affichage de quelques-unes des plus pertinentes pour l'illustration if param in ['REQUEST_METHOD', 'QUERY_STRING', 'REMOTE_ADDR', 'HTTP_USER_AGENT']: print(f"**{param}:** {value}<br>")print("</pre>")print("</body></html>")Méthodes GET et POST
Section titled “Méthodes GET et POST”Ce sont les deux méthodes HTTP les plus courantes pour envoyer des données d’un navigateur au serveur :
Méthode GET
Section titled “Méthode GET”Les données sont ajoutées à l’URL sous forme de chaîne de requête (par exemple, /search?query=python&page=1).
- Visible dans la barre d’adresse du navigateur et les journaux du serveur.
- Convient aux données non sensibles comme les termes de recherche ou les numéros de page.
- Longueur limitée (dépend du navigateur/serveur, souvent autour de 2048 caractères).
- Idempotente (la répétition de la requête devrait idéalement avoir le même effet).
- Dans le CGI, les données se trouvaient dans la variable d’environnement
QUERY_STRING. - Dans les frameworks modernes, l’accès se fait via des attributs d’objet de requête comme
request.args.
Méthode POST
Section titled “Méthode POST”Les données sont envoyées dans le corps de la requête HTTP.
- Non visible dans l’URL.
- Convient aux données sensibles (comme les mots de passe) ou aux grandes quantités de données (par exemple, téléchargements de fichiers, formulaires longs).
- Pas de limite de longueur pratique.
- Pas nécessairement idempotente (la répétition pourrait exécuter à nouveau l’action, par exemple, soumettre une commande).
- Dans le CGI, les données étaient lues depuis l’entrée standard, avec une longueur spécifiée par
CONTENT_LENGTH. - Dans les frameworks modernes, l’accès se fait via des attributs comme
request.form(pour les données de formulaire) ourequest.data/request.json(pour les corps bruts/JSON).
Gestion des données de formulaire (Conceptuel)
Section titled “Gestion des données de formulaire (Conceptuel)”Les formulaires web permettent aux utilisateurs de saisir des données. Lorsqu’ils sont soumis, ces données sont envoyées en utilisant la méthode GET ou POST.
Exemple de formulaire HTML :
<!DOCTYPE html><html><head><title>Form Example</title></head><body> <form action="/process-form" method="post"> <label for="fname">First Name:</label> <input type="text" id="fname" name="first_name"><br><br> <label for="lname">Last Name:</label> <input type="text" id="lname" name="last_name"><br><br> <input type="submit" value="Submit"> </form></body></html>Extrait Python (Illustrant la logique d’accès aux données - Style Framework) :
# Supposons que 'request' est un objet fourni par un framework web
# Exemple d'accès aux données POST (par exemple, style Flask/Django)# first_name = request.form.get('first_name', 'Guest')# last_name = request.form.get('last_name', '')
# Exemple d'accès aux données GET (par exemple, style Flask/Django)# query = request.args.get('query', '')
def process_form_data(form_data): """Fonction conceptuelle pour traiter les données de formulaire analysées.""" first_name = form_data.get('first_name', 'Guest') last_name = form_data.get('last_name', '')
# Génère la réponse (les frameworks gèrent les détails de l'en-tête) response_body = f"<!DOCTYPE html><html><head><title>Formulaire Reçu</title></head>" response_body += f"<body><h2>Bonjour {first_name} {last_name}</h2></body></html>" return response_body
# Dans une vraie application, le framework routerait la requête et# fournirait les données de formulaire analysées (par exemple, request.form) à une fonction de gestion.# Appel simulé d'exemple :# received_data = {'first_name': 'Zara', 'last_name': 'Ali'}# html_output = process_form_data(received_data)# print(html_output) # Le framework enverrait ceci comme réponseGestion des différents éléments de formulaire (Conceptuel)
Section titled “Gestion des différents éléments de formulaire (Conceptuel)”Les cases à cocher, les boutons radio, les zones de texte et les listes déroulantes sont gérés de manière similaire : leur attribut name devient la clé, et leur attribut value (ou le contenu pour les textarea) devient la valeur. Les cases à cocher peuvent envoyer plusieurs valeurs pour le même nom si elles sont sélectionnées, ou ne pas envoyer la clé du tout si elles sont désélectionnées. Les frameworks les analysent en structures de données appropriées (souvent des objets de type dictionnaire).
Utilisation des cookies (Conceptuel)
Section titled “Utilisation des cookies (Conceptuel)”Les cookies sont de petits morceaux de données stockés par le navigateur, renvoyés au serveur avec les requêtes ultérieures. Ils sont utilisés pour maintenir l’état (comme les sessions utilisateur) à travers des requêtes HTTP sans état.
- Définition des cookies : Effectuée via l’en-tête de réponse HTTP
Set-Cookie. Inclut le nom/valeur du cookie et des attributs optionnels commeExpires,Max-Age,Domain,Path,Secure,HttpOnly,SameSite. - Récupération des cookies : Le navigateur renvoie les cookies stockés au serveur dans l’en-tête de requête HTTP
Cookie. - Pratique moderne : Les frameworks offrent des méthodes pour définir et récupérer facilement les cookies (par exemple,
response.set_cookie(...)etrequest.cookies.get(...)).
Téléversements de fichiers (Conceptuel)
Section titled “Téléversements de fichiers (Conceptuel)”Les formulaires HTML avec enctype="multipart/form-data" et <input type="file"> permettent aux utilisateurs de téléverser des fichiers.
Le serveur reçoit les données du fichier dans le corps de la requête. À l’époque du CGI, l’analyse des données multiparties était complexe. Les frameworks modernes facilitent l’accès aux fichiers téléversés via l’objet de requête (par exemple, request.files dans Flask/Django), gérant les complexités de l’analyse.
Exemple : Traitement d’un fichier téléversé (Logique de style Framework)
# Supposons que 'request' est un objet de requête de framework# Supposons que 'secure_filename' est un utilitaire pour assainir les noms de fichiers# Supposons que UPLOAD_FOLDER est un chemin configuré
# file_storage = request.files.get('filename')
def save_uploaded_file(file_storage): message = "Aucun fichier n'a été téléversé." if file_storage and file_storage.filename: try: # Il est crucial d'assainir les noms de fichiers provenant de l'entrée utilisateur # filename = secure_filename(file_storage.filename) # save_path = os.path.join(UPLOAD_FOLDER, filename) # file_storage.save(save_path) filename = file_storage.filename # Dans le code réel, assainissez ceci ! message = f'Le fichier "{filename}" a été téléversé avec succès (simulé).' # Dans une vraie application, vous utiliseriez file_storage.save() except Exception as e: message = f"Une erreur s'est produite : {e}"
response_body = f"<!DOCTYPE html><html><body><p>{message}</p></body></html>" return response_body
# Appel simulé :# class MockFileStorage:# filename = 'example.txt'# def save(self, path): print(f'Sauvegarde simulée vers {path}')## uploaded_file = MockFileStorage()# html_output = save_uploaded_file(uploaded_file)# print(html_output)Note de sécurité : Validez et assainissez toujours les entrées utilisateur, en particulier les noms de fichiers et le contenu des fichiers, afin de prévenir les vulnérabilités de sécurité comme le directory traversal (traversée de répertoire).
Téléchargements de fichiers (Conceptuel)
Section titled “Téléchargements de fichiers (Conceptuel)”Pour inciter un navigateur à télécharger un fichier au lieu de l’afficher, le serveur envoie des en-têtes HTTP spécifiques :
Content-Type: application/octet-stream(ou un type MIME plus spécifique si connu) indique au navigateur qu’il s’agit de données binaires.Content-Disposition: attachment; filename="your_file_name.ext"suggère le nom de fichier pour la boîte de dialogue de téléchargement.
Les frameworks modernes fournissent généralement des fonctions d’aide (par exemple, send_file dans Flask) pour générer ces en-têtes et diffuser correctement le contenu du fichier.
Conclusion
Section titled “Conclusion”Bien que la programmation CGI directe en Python soit dépassée, les concepts sous-jacents de gestion des requêtes, de traitement des données, de gestion de l’état (cookies) et de génération de réponses restent fondamentaux pour le développement web. Les frameworks web Python modernes s’appuient sur ces concepts, offrant des abstractions beaucoup plus efficaces, sécurisées et conviviales pour les développeurs.
Pour aller plus loin :
- Explorez les frameworks web Python populaires : Flask, Django, FastAPI.
- Découvrez WSGI (Web Server Gateway Interface) et ASGI (Asynchronous Server Gateway Interface).