Programmation CGI Python


Qu'est-ce que CGI

CGI est actuellement maintenu par NCSA, et NCSA définit CGI comme suit :

CGI (Common Gateway Interface), une interface de passerelle générale, est un programme qui s'exécute sur un serveur tel qu'un serveur HTTP, fournissant une interface aux pages HTML du client.


Navigation Web

Pour mieux comprendre comment fonctionne CGI, nous pouvons examiner le processus de clic sur un lien ou une URL sur une page Web :

  • 1. Utilisez votre navigateur pour accéder à l'URL et vous connecter au serveur web HTTP.
  • 2. Après avoir reçu la requête, le serveur web analyse l'URL et vérifie si le fichier accédé existe sur le serveur. S'il existe, il renvoie le contenu du fichier ; sinon, il renvoie le message d'erreur correspondant.
  • 3. Le navigateur reçoit les informations du serveur et affiche le fichier reçu ou le message d'erreur.

Les programmes CGI peuvent être des scripts Python, des scripts PERL, des scripts SHELL, des programmes C ou C++, etc.


Schéma de l'architecture CGI

cgiarch


Prise en charge et configuration du serveur web

Avant de commencer la programmation CGI, assurez-vous que votre serveur web prend en charge CGI et qu'un gestionnaire CGI est configuré.

Configuration CGI d'Apache:

Configurer le répertoire CGI:

ScriptAlias /cgi-bin/ /var/www/cgi-bin/

Tous les programmes CGI exécutés par les serveurs HTTP sont stockés dans un répertoire préconfiguré. Ce répertoire est appelé répertoire CGI et, par convention, il est nommé /var/www/cgi-bin.

Les fichiers CGI ont l'extension .cgi, et Python peut aussi utiliser l'extension .py.

Par défaut, les serveurs Linux sont configurés pour exécuter le répertoire cgi-bin dans /var/www.

Si vous souhaitez spécifier un autre répertoire pour exécuter des scripts CGI, vous pouvez modifier le fichier de configuration httpd.conf comme suit :

<Directory "/var/www/cgi-bin">
   AllowOverride None
   Options +ExecCGI
   Order allow,deny
   Allow from all
</Directory>

Ajoutez le suffixe .py à AddHandler, afin que nous puissions accéder aux fichiers de script Python se terminant par .py :

AddHandler cgi-script .cgi .pl .py

Premier programme CGI

Nous utilisons Python pour créer notre premier programme CGI, nommé hello.py, situé dans le répertoire /var/www/cgi-bin, avec le contenu suivant :

Exemple

#!/usr/bin/python3

print ("Content-type:text/html")
print ()                             # Ligne vide, indique au serveur que l'en-tête est terminé
print ('<html>')
print ('<head>')
print ('<meta charset="utf-8">')
print ('<title>Bonjour le monde - Mon premier programme CGI !</title>')
print ('</head>')
print ('<body>')
print ('<h2>Bonjour le monde ! Je suis le premier programme CGI du tutoriel Example</h2>')
print ('</body>')
print ('</html>')

Après avoir enregistré le fichier, modifiez hello.py et définissez les autorisations du fichier sur 755 :

chmod 755 hello.py 

Le résultat de l'accès au programme ci-dessus dans un navigateur est le suivant :

Le script hello.py est un simple script Python. La première ligne de sortie, "Content-type:text/html", est envoyée au navigateur pour lui indiquer que le type de contenu affiché est "text/html".

Utilisez print pour afficher une ligne vide afin d'indiquer au serveur que les informations d'en-tête sont complètes.


En-têtes HTTP

"Content-type:text/html" dans le fichier hello.py fait partie de l'en-tête HTTP. Il est envoyé au navigateur pour lui indiquer le type de contenu du fichier.

Le format de l'en-tête HTTP est le suivant :

HTTP 字段名: 字段内容

Par exemple :

Content-type: text/html

Le tableau suivant décrit les informations couramment utilisées dans les en-têtes HTTP des programmes CGI :

headDescription
Content-type: Le type MIME correspondant à l'entité de la requête. Par exemple : Content-type:text/html
Expires: Date La date et l'heure auxquelles la réponse expire
Location: URL Utilisé pour rediriger le destinataire vers un emplacement autre que l'URL demandée afin de terminer la requête ou d'identifier une nouvelle ressource
Last-modified: DateLa date de dernière modification de la ressource demandée
Content-length: NLa longueur du contenu de la requête
Set-Cookie: Chaîne Définir un cookie HTTP

Variables d'environnement CGI

Tous les programmes CGI reçoivent les variables d'environnement suivantes, qui jouent un rôle important dans les programmes CGI :

Nom de la variableDescription
CONTENT_TYPELa valeur de cette variable d'environnement indique le type MIME des informations transmises. Actuellement, la variable d'environnement CONTENT_TYPE est généralement : application/x-www-form-urlencoded, ce qui signifie que les données proviennent d'un formulaire HTML.
CONTENT_LENGTHLorsque le serveur communique avec le programme CGI via POST, cette variable d'environnement indique le nombre d'octets de données valides pouvant être lus depuis l'entrée standard (STDIN). Cette variable doit être utilisée lors de la lecture des données d'entrée.
HTTP_COOKIELe contenu COOKIE sur la machine cliente.
HTTP_USER_AGENTFournit des informations sur le navigateur client, y compris les numéros de version ou d'autres données propriétaires.
PATH_INFOLa valeur de cette variable d'environnement représente des informations de chemin supplémentaires qui suivent le nom du programme CGI. Elle est souvent utilisée comme paramètre du programme CGI.
QUERY_STRINGSi le serveur transmet des informations au programme CGI en utilisant la méthode GET, la valeur de cette variable d'environnement est l'information transmise. Cette information suit le nom du programme CGI, séparée par un point d'interrogation '?'.
REMOTE_ADDRLa valeur de cette variable d'environnement est l'adresse IP du client effectuant la requête, par exemple 192.168.1.67 ci-dessus. Cette valeur est toujours présente. C'est le seul identifiant que le client web doit fournir au serveur web, et il peut être utilisé dans les programmes CGI pour distinguer différents clients web.
REMOTE_HOSTLa valeur de cette variable d'environnement contient le nom d'hôte du client envoyant la requête CGI. Si vous ne souhaitez pas l'interroger, cette variable d'environnement n'a pas besoin d'être définie.
REQUEST_METHODFournit la méthode par laquelle le script a été appelé. Pour les scripts utilisant le protocole HTTP/1.0, seuls GET et POST sont pertinents.
SCRIPT_FILENAMELe chemin complet du script CGI
SCRIPT_NAMELe nom du script CGI
SERVER_NAMEIl s'agit du nom d'hôte, de l'alias ou de l'adresse IP de votre serveur web.
SERVER_SOFTWARELa valeur de cette variable d'environnement contient le nom et le numéro de version du serveur HTTP qui a appelé le programme CGI. Par exemple, la valeur ci-dessus est Apache/2.2.14(Unix)

Voici un script CGI simple qui affiche les variables d'environnement CGI :

Exemple

#!/usr/bin/python3

import os

print ("Content-type: text/html")
print ()
print ("<meta charset=\"utf-8\">")
print ("<b>Variables d'environnement</b><br>")
print ("<ul>")
for key in os.environ.keys():
    print ("<li><span style='color:green'>%30s </span> : %s </li>" % (key,os.environ[key]))
print ("</ul>")

Enregistrez le code ci-dessus sous test.py, définissez les permissions du fichier sur 755, et le résultat de l'exécution est le suivant :


Méthodes GET et POST

Les clients navigateur transmettent des informations au serveur à l'aide de deux méthodes : la méthode GET et la méthode POST.

Transmission de données à l'aide de la méthode GET

La méthode GET envoie des informations utilisateur encodées au serveur. Les données sont contenues dans l'URL de la page demandée, séparées par un signe "?", comme indiqué ci-dessous :

http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
Quelques autres remarques sur les requêtes GET :
  • Les requêtes GET peuvent être mises en cache
  • Les requêtes GET restent dans l'historique du navigateur
  • Les requêtes GET peuvent être ajoutées aux favoris
  • Les requêtes GET ne doivent pas être utilisées lors du traitement de données sensibles
  • Les requêtes GET ont des limitations de longueur
  • Les requêtes GET doivent être utilisées uniquement pour récupérer des données

Exemple d'URL simple : méthode GET

Voici une URL simple qui utilise la méthode GET pour envoyer deux paramètres au programme hello_get.py :

/cgi-bin/hello_get.py?name=Example&url=http://www.example.com

Voici le code du fichier hello_get.py :

Exemple

#!/usr/bin/python3

# Module de traitement CGI
import cgi, cgitb

# Créer une instance de FieldStorage
form = cgi.FieldStorage()

# Récupérer les données
site_name = form.getvalue('name')
site_url  = form.getvalue('url')

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print (<title>Exemple de test CGI</title>)
print ("</head>")
print ("<body>")
print (<h2>%s site officiel : %s</h2> % (site_name, site_url))
print ("</body>")
print ("</html>")

Après avoir enregistré le fichier, modifiez hello_get.py et changez les permissions du fichier à 755 :

chmod 755 hello_get.py 

Résultat de sortie de la requête du navigateur :

Exemple de formulaire simple : méthode GET

Voici un formulaire HTML qui utilise la méthode GET pour envoyer deux données au serveur. Le script serveur soumis est également le fichier hello_get.py. Le code hello_get.html est le suivant :

Exemple

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Exemple (example.com)</title>
</head>
<body>
<form action="/cgi-bin/hello_get.py" method="get">
Nom du site :<input type="text" name="name">  <br />

URL du site :<input type="text" name="url" />
<input type="submit" value=Envoyer />
</forme>
</body>
</html>

Par défaut, le répertoire cgi-bin ne peut stocker que des fichiers de script. Nous stockons hello_get.html dans le répertoire test et changeons les permissions du fichier à 755 :

chmod 755 hello_get.html

La démo GIF est comme indiqué ci-dessous :

Passer des données à l'aide de la méthode POST

L'utilisation de la méthode POST pour transmettre des données au serveur est plus sûre et plus fiable. Pour les informations sensibles telles que les mots de passe des utilisateurs, POST doit être utilisé pour transmettre les données.

Ce qui suit est également hello_get.py. Il peut également gérer les données de formulaire POST soumises par le navigateur :

Exemple

#!/usr/bin/python3

# Module de traitement CGI
import cgi, cgitb

# Créer une instance de FieldStorage
form = cgi.FieldStorage()

# Récupérer les données
site_name = form.getvalue('name')
site_url  = form.getvalue('url')

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print (<title>Exemple de test CGI</title>)
print ("</head>")
print ("<body>")
print (<h2>%s site officiel : %s</h2> % (site_name, site_url))
print ("</body>")
print ("</html>")

Ce qui suit est un formulaire utilisant la méthode POST (method="post") pour soumettre des données au script serveur hello_get.py :

Exemple

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Exemple (example.com)</title>
</head>
<body>
<form action="/cgi-bin/hello_get.py" method="post">
Nom du site :<input type="text" name="name">  <br />

URL du site :<input type="text" name="url" />
<input type="submit" value=Envoyer />
</forme>
</body>
</html>
</forme>

La démo GIF est comme indiqué ci-dessous :

Transmission des données de case à cocher via un programme CGI

La case à cocher est utilisée pour soumettre une ou plusieurs données d'option. Le code HTML est le suivant :

Exemple

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Exemple (example.com)</title>
</head>
<body>
<form action="/cgi-bin/checkbox.py" method="POST" target="_blank">
<input type="checkbox" name="example" value="on" />Exemple
<input type="checkbox" name="google" value="on" /> Google
<input type="submit" value=Sélectionner le site />
</forme>
</body>
</html>

Voici le code du fichier checkbox.py :

Exemple

#!/usr/bin/python3

# Importer le module de traitement CGI
import cgi, cgitb

# Créer une instance de FieldStorage
form = cgi.FieldStorage()

# Recevoir les données des champs
if form.getvalue('google'):
   google_flag = Oui
else:
   google_flag = Non

if form.getvalue('example'):
   example_flag = Oui
else:
   example_flag = Non

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print (<title>Exemple de test CGI</title>)
print ("</head>")
print ("<body>")
print (<h2> Si Exemple est sélectionné : %s</h2> % example_flag)
print (<h2> Si Google est sélectionné : %s</h2> % google_flag)
print ("</body>")
print ("</html>")

Modifier les permissions de checkbox.py :

chmod 755 checkbox.py

Démo GIF d'accès au navigateur :

Transmission de données de boutons radio via un programme CGI

Un bouton radio n'envoie qu'une seule donnée au serveur. Le code HTML est le suivant :

Exemple

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Exemple (example.com)</title>
</head>
<body>
<form action="/cgi-bin/radiobutton.py" method="post" target="_blank">
<input type="radio" name="site" value="example" />Exemple
<input type="radio" name="site" value="google" /> Google
<input type="submit" value=Envoyer />
</forme>
</body>
</html>

Le code du script radiobutton.py est le suivant :

Exemple

#!/usr/bin/python3

# Importer le module de traitement CGI
import cgi, cgitb

# Créer une instance de FieldStorage
form = cgi.FieldStorage()

# Recevoir les données des champs
if form.getvalue('site'):
   site = form.getvalue('site')
else:
   site = Les données soumises sont vides

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print (<title>Exemple de test CGI</title>)
print ("</head>")
print ("<body>")
print (<h2> Le site Web sélectionné est %s</h2> % site)
print ("</body>")
print ("</html>")

Modifier les permissions de radiobutton.py :

chmod 755 radiobutton.py

Démo GIF d'accès au navigateur :

Transmission de données Textarea via un programme CGI

Textarea envoie des données multilignes au serveur. Le code HTML est le suivant :

Exemple

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Exemple (example.com)</title>
</head>
<body>
<form action="/cgi-bin/textarea.py" method="post" target="_blank">
<textarea name="textcontent" cols="40" rows="4">
Entrez le contenu ici...
</textarea>
<input type="submit" value=Envoyer />
</forme>
</body>
</html>

Le code du script textarea.py est le suivant :

Exemple

#!/usr/bin/python3

# Importer le module de traitement CGI
import cgi, cgitb

# Créer une instance de FieldStorage
form = cgi.FieldStorage()

# Recevoir les données des champs
if form.getvalue('textcontent'):
   text_content = form.getvalue('textcontent')
else:
   text_content = Aucun contenu

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print (<title>Exemple de test CGI</title>)
print ("</head>")
print ("<body>")
print (<h2> L'entrée est : %s</h2> % text_content)
print ("</body>")
print ("</html>")
>

Modifiez les permissions de textarea.py :

chmod 755 textarea.py

Démo GIF d'accès au navigateur :

Transmission de données de liste déroulante via un programme CGI.

Le code de la liste déroulante HTML est le suivant :

Exemple

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Exemple (example.com)</title>
</head>
<body>
<form action="/cgi-bin/dropdown.py" method="post" target="_blank">
<select name="dropdown">
<option value="example" selected>Exemple</option>
<option value="google">Google</option>
</select>
<input type="submit" value=Envoyer/>
</forme>
</body>
</html>

Le code du script dropdown.py est le suivant :

Exemple

#!/usr/bin/python3

# Importer le module de traitement CGI
import cgi, cgitb

# Créer une instance de FieldStorage
form = cgi.FieldStorage()

# Recevoir les données des champs
if form.getvalue('dropdown'):
   dropdown_value = form.getvalue('dropdown')
else:
   dropdown_value = Aucun contenu

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print (<title>Exemple de test CGI</title>)
print ("</head>")
print ("<body>")
print (<h2> L'option sélectionnée est : %s</h2> % dropdown_value)
print ("</body>")
print ("</html>")

Modifier les permissions de dropdown.py :

chmod 755 dropdown.py

Démo GIF d'accès au navigateur :


Utilisation des cookies en CGI

Un inconvénient majeur du protocole HTTP est qu'il ne détermine pas l'identité de l'utilisateur, ce qui cause de grands désagréments aux programmeurs. L'apparition de la fonctionnalité des cookies comble cette lacune.

Un cookie consiste à écrire des données d'enregistrement sur le disque dur du client via le navigateur du client pendant que celui-ci accède à un script. Lors de la prochaine visite du client sur le script, les informations de données sont récupérées, ce qui permet de distinguer l'identité. Les cookies sont couramment utilisés pour la vérification d'identité.

 

Syntaxe des cookies

Les cookies HTTP sont envoyés via l'en-tête HTTP, qui précède le transfert de fichier. La syntaxe de l'en-tête Set-Cookie est la suivante :

Set-cookie:name=name;expires=date;path=path;domain=domain;secure 
  • name=name :La valeur du cookie doit être définie (le nom ne peut pas utiliser ";et,signe), lorsqu'il y a plusieurs valeurs de nom, utilisez;; pour les séparer, par exemple :name1=name1;name2=name2;name3=name3。
  • expires=date:Période de validité du cookie, format : expires="Wdy,DD-Mon-YYYY HH:MM:SS"
  • path=path: Définissez le chemin pris en charge par le cookie. Si path est un répertoire, le cookie prend effet pour tous les fichiers et sous-répertoires de ce répertoire, par exemple : path="/cgi-bin/". Si path est un fichier, le cookie ne prend effet que pour ce fichier, par exemple : path="/cgi-bin/cookie.cgi".
  • domain=domain:Le domaine pour lequel le cookie est valide, par exemple : domain="www.example.com"
  • secure:Si cet indicateur est donné, cela signifie que le cookie ne peut être transmis que via un serveur HTTPS utilisant le protocole SSL.
  • La réception des cookies est assurée en définissant la variable d'environnement HTTP_COOKIE. Les programmes CGI peuvent obtenir les informations de cookie en interrogeant cette variable.

Définition des cookies

La définition de cookies est très simple. Les cookies sont envoyés séparément dans l'en-tête HTTP. L'exemple suivant définit name et expires dans le cookie :

Exemple

#!/usr/bin/python3

print ('Set-Cookie: name="Example";expires=Wed, 28 Aug 2016 18:30:00 GMT')
print ('Content-Type: text/html')

print ()
print ("""
<html>
  <head>
    <meta charset="utf-8">
<title>Exemple (example.com)</title>
  </head>
    <body>
        <h1>Cookie défini avec succès !</h1>
    </body>
</html>
"""
)

Enregistrez le code ci-dessus dans cookie_set.py et modifiez les permissions de cookie_set.py :

chmod 755 cookie_set.py

L'exemple ci-dessus utilise les informations d'en-tête Set-Cookie pour définir les informations de cookie. Les éléments facultatifs définissent d'autres attributs de cookie, tels que l'heure d'expiration Expires, le domaine Domain, le chemin Path. Ces informations sont définies"Content-type:text/html"avant.


Récupération des informations de cookie

La récupération des informations de cookie est très simple. Les informations de cookie sont stockées dans la variable d'environnement CGI HTTP_COOKIE. Le format de stockage est le suivant :

key1=value1;key2=value2;key3=value3....

Voici un simple programme CGI pour récupérer les informations de cookie :

Exemple

#!/usr/bin/python3

# Importer le module
import os
import http.cookies

print ("Content-type: text/html")
print ()

print ("""
<html>
<head>
<meta charset="utf-8">
<title>Tutoriel d'exemple (example.com)</title>
</head>
<body>
<h1>Lire les informations des cookies</h1>
"""
)

if 'HTTP_COOKIE' in os.environ:
    cookie_string=os.environ.get('HTTP_COOKIE')
    c= http.cookies.SimpleCookie()
   # c=Cookie.SimpleCookie()
    c.load(cookie_string)

    try:
        data=c['name'].value
        print ("données du cookie : "+data+"<br>")
    except KeyError:
        print ("Cookie non défini ou expiré<br>")
print ("""
</body>
</html>
"""
)

Enregistrez le code ci-dessus dans cookie_get.py et modifiez les permissions de cookie_get.py :

chmod 755 cookie_get.py

Le GIF démontrant la configuration des cookies ci-dessus est le suivant :

Exemple de téléversement de fichier

Les formulaires HTML pour le téléversement de fichiers doivent définirenctypel'attribut àmultipart/form-data, le code est le suivant :

Exemple

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tutoriel d'exemple (example.com)</title>
</head>
<body>
 <form enctype="multipart/form-data"
                    action="/cgi-bin/save_file.py" method="post">
   <p>Sélectionner un fichier :<input type="file" name="filename" /></p>
   <p><input type="submit" value="Téléverser" /></p>
   </forme>
</body>
</html>

Le code du fichier de script save_file.py est le suivant :

Exemple

#!/usr/bin/python3

import cgi, os
import cgitb; cgitb.enable()

form = cgi.FieldStorage()

# Obtenir le nom du fichier
fileitem = form['filename']

# Vérifier si le fichier a été téléversé
if fileitem.filename:
   # Définir le chemin du fichier
   fn = os.path.basename(fileitem.filename)
   open('/tmp/' + fn, 'wb').write(fileitem.file.read())

   message = 'Fichier "' + fn + '" téléversé avec succès'
   
else:
   message = 'Fichier non téléversé'
   
print ("""\
Content-Type: text/html\n
<html>
<head>
<meta charset="utf-8">
<title>Tutoriel d'exemple (example.com)</title>
</head>
<body>
   <p>%s</p>
</body>
</html>
"""
% (message,))

Enregistrez le code ci-dessus dans save_file.py, et modifiez les permissions de save_file.py :

chmod 755 save_file.py

Le GIF démontrant la configuration des cookies ci-dessus est le suivant :

Si votre système est Unix/Linux, vous devez remplacer le séparateur de fichiers ; sous Windows, vous n'avez qu'à utiliser l'instruction open() :

fn = os.path.basename(fileitem.filename.replace("\\", "/" ))

Boîte de dialogue de téléchargement de fichier

Tout d'abord, créez un fichier foo.txt dans le répertoire courant pour que le programme puisse le télécharger.

Le téléchargement de fichier est implémenté en définissant les informations d'en-tête HTTP. Le code fonctionnel est le suivant :

Exemple

#!/usr/bin/python3

# En-têtes HTTP
print ("Content-Disposition: attachment; filename=\"foo.txt\"")
print ()
# Ouvrir le fichier
fo = open("foo.txt", "rb")

str = fo.read();
print (str)

# Fermer le fichier
fo.close()
Autres extensions