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) est un programme qui s'exécute sur un serveur, tel qu'un serveur HTTP, fournissant une interface avec les pages HTML client.

Remarque :

À partir de Python 3.11, le module cgi est officiellement marqué comme « Deprecated » et a été supprimé à partir de Python 3.13.

Il est recommandé de migrer versFlask、DjangoouFastAPIet d'autres frameworks Web modernes.


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 les informations de 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 un message d'erreur.
  • 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 que le gestionnaire CGI est configuré.

Apache prend en charge la configuration CGI :

Configurer le répertoire CGI:

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

Les programmes CGI exécutés par tous 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.

L'extension de fichier pour CGI est.cgi, Python peut également utiliser l'extension.py.py.

Par défaut, le répertoire cgi-bin configuré pour s'exécuter sur les serveurs Linux est /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 dans 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 :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

print "Content-type:text/html"
print                               # 空行,告诉服务器结束头部
print '<html>'
print '<head>'
print '<meta charset="utf-8">'
print '<title>Hello World - 我的第一个 CGI 程序!</title>'
print '</head>'
print '<body>'
print '<h2>Hello World! 我是来自Example的第一CGI程序</h2>'
print '</body>'
print '</html>'

Après avoir enregistré le fichier, modifiez hello.py et changez ses permissions de fichier à 755 :

chmod 755 hello.py 

Accédez au programme ci-dessus dans le navigateur à l'adressehttp://localhost/cgi-bin/hello.pyLe résultat affiché est le suivant :

Hello World! 我是来自Example的第一CGI程序

Ce script hello.py est un simple script Python. La sortie "Content-type:text/html" sur la première ligne du script est envoyée au navigateur et indique au navigateur que le type de contenu à afficher est "text/html".

Utilisez print pour afficher une ligne vide afin d'indiquer au serveur que les informations d'en-tête sont terminées.


En-tête HTTP

Le "Content-type:text/html" dans le contenu du 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: Les informations MIME correspondant à l'entité demandée. 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 indique que les données proviennent d'un formulaire HTML.
CONTENT_LENGTHSi le serveur et le programme CGI communiquent en utilisant la méthode POST, cette variable d'environnement est le nombre d'octets de données valides pouvant être lus depuis l'entrée standard STDIN. Cette variable d'environnement doit être utilisée lors de la lecture des données d'entrée.
HTTP_COOKIELe contenu COOKIE dans le client.
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 suivant immédiatement le nom du programme CGI. Elle apparaît souvent comme un paramètre du programme CGI.
QUERY_STRINGSi le serveur et le programme CGI communiquent 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. De plus, c'est l'identifiant unique que le client Web doit fournir au serveur Web, et il peut être utilisé dans le programme 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 elle ne prend pas en charge la requête que vous souhaitez effectuer, cette variable d'environnement n'a pas besoin d'être définie.
REQUEST_METHODFournit la méthode par laquelle le script est appelé. Pour les scripts utilisant le protocole HTTP/1.0, seuls GET et POST sont significatifs.
SCRIPT_FILENAMELe chemin complet du script CGI
SCRIPT_NAMELe nom du script CGI
SERVER_NAMEC'est le nom d'hôte, l'alias ou 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 appelle 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 :

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# filename:test.py

import os

print "Content-type: text/html"
print
print "<meta charset=\"utf-8\">"
print "<b>环境变量</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, et modifiez les permissions du fichier à 755. Le résultat de l'exécution est le suivant :


Méthodes GET et POST

Le client navigateur transmet des informations au serveur par deux méthodes, à savoir la méthode GET et la méthode POST.

Utilisation de la méthode GET pour transférer des données

La méthode GET envoie des informations utilisateur encodées au serveur. Les données sont incluses 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 une limite 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/test.py?name=Example&url=http://www.example.com

Voici le code du fichier hello_get.py :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# filename:test.py

# CGI处理模块
import cgi, cgitb 

# 创建 FieldStorage 的实例化
form = cgi.FieldStorage() 

# 获取数据
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>Example CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2>%s官网:%s</h2>" % (site_name, site_url)
print "</body>"
print "</html>"

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

chmod 755 hello_get.py 

Le résultat de sortie de la requête du navigateur est :

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 pour la soumission est également le fichier hello_get.py. Le code de hello_get.html est le suivant :

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example(example.com)</title>
</head>
<body>
<form action="../cgi-bin/hello_get.py" method="get">
站点名称: <input type="text" name="name">  <br />

站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>

Par défaut, le répertoire cgi-bin ne peut contenir 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émonstration Gif est présentée ci-dessous :

Utilisation de la méthode POST pour transférer des données

Utiliser la méthode POST pour transmettre des données au serveur est plus sûr et plus fiable. Les informations sensibles telles que les mots de passe des utilisateurs doivent être transmises à l'aide de POST.

Voici également hello_get.py, qui peut aussi gérer les données de formulaire POST soumises par le navigateur :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# CGI处理模块
import cgi, cgitb 

# 创建 FieldStorage 的实例化
form = cgi.FieldStorage() 

# 获取数据
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>Example CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2>%s官网:%s</h2>" % (site_name, site_url)
print "</body>"
print "</html>"

Le formulaire suivant utilise la méthode POST (method="post") pour soumettre des données au script serveur hello_get.py :

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example(example.com)</title>
</head>
<body>
<form action="../cgi-bin/hello_get.py" method="post">
站点名称: <input type="text" name="name">  <br />

站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>

La démonstration Gif est présentée ci-dessous :

Transmission de données de cases à cocher via un programme CGI

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

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example(example.com)</title>
</head>
<body>
<form action="../cgi-bin/checkbox.py" method="POST" target="_blank">
<input type="checkbox" name="example" value="on" /> Example
<input type="checkbox" name="google" value="on" /> Google
<input type="submit" value="选择站点" />
</form>
</body>
</html>

Voici le code du fichier checkbox.py :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('google'):
   google_flag = "是"
else:
   google_flag = "否"

if form.getvalue('example'):
   example_flag = "是"
else:
   example_flag = "否"

print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Example CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> Example是否选择了 : %s</h2>" % example_flag
print "<h2> Google 是否选择了 : %s</h2>" % google_flag
print "</body>"
print "</html>"

Modifiez les permissions de checkbox.py :

chmod 755 checkbox.py

Démonstration GIF de l'accès via navigateur :

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

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

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

Le code du script radiobutton.py est le suivant :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('site'):
   site = form.getvalue('site')
else:
   site = "提交数据为空"

print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Example CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 选中的网站是 %s</h2>" % site
print "</body>"
print "</html>"

Modifiez les permissions de radiobutton.py :

chmod 755 radiobutton.py

Démonstration GIF de l'accès via navigateur :

Transmission de données de zone de texte via un programme CGI

La zone de texte transmet des données multilignes au serveur. Le code HTML est le suivant :

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example(example.com)</title>
</head>
<body>
<form action="../cgi-bin/textarea.py" method="post" target="_blank">
<textarea name="textcontent" cols="40" rows="4">
在这里输入内容...
</textarea>
<input type="submit" value="提交" />
</form>
</body>
</html>

Le code du script textarea.py est le suivant :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('textcontent'):
   text_content = form.getvalue('textcontent')
else:
   text_content = "没有内容"

print "Content-type:text/html"
print
print "<html>"
print "<head>";
print "<meta charset=\"utf-8\">"
print "<title>Example CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 输入的内容是:%s</h2>" % text_content
print "</body>"
print "</html>"

Modifiez les permissions de textarea.py :

chmod 755 textarea.py

Démonstration GIF de l'accès via navigateur :

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

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

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

Le code du script dropdown.py est indiqué ci-dessous :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('dropdown'):
   dropdown_value = form.getvalue('dropdown')
else:
   dropdown_value = "没有内容"

print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Example CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 选中的选项是:%s</h2>" % dropdown_value
print "</body>"
print "</html>"

Modifiez les permissions de dropdown.py :

chmod 755 dropdown.py

Démonstration GIF de l'accès via 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 entraîne une grande gêne pour les programmeurs. L'apparition de la fonction Cookie comble cette lacune.

Un cookie est un enregistrement de données écrites sur le disque dur du client via le navigateur du client lorsque le client accède au script. Lorsque le client accède au script la prochaine fois, 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

L'envoi de cookies HTTP est implémenté via l'en-tête HTTP. Il précède la transmission du 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 (name ne peut pas utiliser les signes ";" et ","). Lorsqu'il y a plusieurs valeurs name, 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 chemin, 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 implémentée en définissant la variable d'environnement HTTP_COOKIE. Le programme CGI peut obtenir les informations du cookie en récupérant cette variable.

Paramètres des cookies

Définir des cookies est très simple. Le cookie est envoyé séparément dans l'en-tête HTTP. L'exemple suivant définit name et expires dans le cookie :

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 
print 'Content-Type: text/html'
print 'Set-Cookie: name="Example";expires=Wed, 28 Aug 2016 18:30:00 GMT'
print
print """
<html>
    <head>
        <meta charset="utf-8">
        <title>Example(example.com)</title>
    </head>
    <body>
        <h1>Cookie set OK!</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 de l'en-tête Set-Cookie pour définir les informations du cookie. Dans les options, d'autres attributs de cookie sont définis, tels que la date d'expiration Expires, le domaine Domain et le chemin Path. Ces informations sont définies avant "Content-type:text/html".


Récupération des informations de cookie

La page de 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 programme CGI simple pour récupérer les informations de cookie :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 导入模块
import os
import Cookie

print "Content-type: text/html"
print

print """
<html>
<head>
<meta charset="utf-8">
<title>Example(example.com)</title>
</head>
<body>
<h1>读取cookie信息</h1>
"""

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

    try:
        data=c['name'].value
        print "cookie data: "+data+"<br>"
    except KeyError:
        print "cookie 没有设置或者已过期<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 illustrant le paramétrage de couleur de cookie ci-dessus est le suivant :

Exemple de téléversement de fichier

Le formulaire HTML pour téléverser des fichiers doit définir l'attributenctypesurmultipart/form-data, et le code est le suivant :

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example(example.com)</title>
</head>
<body>
 <form enctype="multipart/form-data" 
                     action="../cgi-bin/save_file.py" method="post">
   <p>选中文件: <input type="file" name="filename" /></p>
   <p><input type="submit" value="上传" /></p>
   </form>
</body>
</html>

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

#!/usr/bin/python
# -*- coding: UTF-8 -*-

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

form = cgi.FieldStorage()

# 获取文件名
fileitem = form['filename']

# 检测文件是否上传
if fileitem.filename:
   # 设置文件路径 
   fn = os.path.basename(fileitem.filename)
   open('/tmp/' + fn, 'wb').write(fileitem.file.read())

   message = '文件 "' + fn + '" 上传成功'
   
else:
   message = '文件没有上传'
   
print """\
Content-Type: text/html\n
<html>
<head>
<meta charset="utf-8">
<title>Example(example.com)</title>
</head>
<body>
   <p>%s</p>
</body>
</html>
""" % (message,)

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

chmod 755 save_file.py

Le Gif illustrant le paramétrage de couleur de cookie ci-dessus est le suivant :

Si vous utilisez un système Unix/Linux, vous devez remplacer le séparateur de fichiers. Sous Windows, il vous suffit d'utiliser l'instruction open() :

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

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

Nous créons d'abord 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 :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# HTTP 头部
print "Content-Disposition: attachment; filename=\"foo.txt\"";
print
# 打开文件
fo = open("foo.txt", "rb")

str = fo.read();
print str

# 关闭文件
fo.close()
Autres extensions