CGI(Common Gateway Interface)即通用网关接口,按百度百科对其的解释是:
CGI(Common Gateway Interface) 是WWW技术中最重要的技术之一,有着不可替代的重要地位。CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规程。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完整的新的交互式媒体。
Common Gateway Interface,简称CGI。在物理上是一段程序,运行在服务器上,提供同客户端HTML页面的接口。这样说大概还不好理解。那么我们看一个实际例子:现在的个人主页上大部分都有一个留言本。留言本的工作是这样的:先由用户在客户端输入一些信息,如名字之类的东西。接着用户按一下“留言”(到目前为止工作都在客户端),浏览器把这些信息传送到服务器的CGI目录下特定的cgi程序中,于是cgi程序在服务器上按照预定的方法进行处理。在本例中就是把用户提交的信息存入指定的文件中。然后cgi程序给客户端发送一个信息,表示请求的任务已经结束。此时用户在浏览器里将看到“留言结束”的字样。整个过程结束。
而Python的cgi模块定义了使用Python编写CGI脚本的实用工具。
1. CGI简介
CGI脚本通常是由服务器调用用来处理处理用户通过表单或者<ISINDEX>
元素提交的用户输入的。
通常情况下,CGI脚本存放于服务器的cgi-bin
文件夹下(这个文件夹下也同时存储着脚本shell运行环境下和请求有关的所有种类的信息)。
脚本有两种输入方式:通过表单提交或者包含在URL中,对这两种方式cgi模块都提供了简单的接口进行处理。
CGI脚本的输出应该包含通过一个空行分隔的两个部分:第一个部分包含了一些请求头,以此告诉客户端接下返回信息的类型,下面是使用python生成简单头信息的例子:
print("Content-Type: text/html") # 接下来返回html
print() # 空行,表示头信息结束
第二部分通常是HTML,使客户端以美观的格式输出文本、内联图片等,下面是python生成简单的HTML:
print("<TITLE>CGI script output</TITLE>")
print("<H1>This is my first CGI script</H1>")
print("Hello, world!")
2. 使用cgi模块
在新建一个cgi脚本前,推荐在开头写上下面的代码:
import cgitb
cgitb.enable()
它会启动一个特殊的异常处理句柄使得错误发生时将具体信息在Web浏览器上显示出来。如果不想显示在浏览器上,可以指定logfile
将它以日志的形式存储下来。
import cgitb
cgitb.enable(display=0, logdir="/path/to/logdir")
可以使用FieldStorage
类获得表单提交的数据, FieldStorage
可以表现得像python的字典一样, 可以使用in
遍历、keys()
和内建的len()
方法。下面的代码(假设Content-Type
头和空行已经打印)检测了name
和addr
字段是否是空字符串:
form = cgi.FieldStorage()
if "name" not in form or "addr" not in form:
print("<H1>Error</H1>")
print("Please fill in the name and addr fields.")
return
print("<p>name:", form["name"].value)
print("<p>addr:", form["addr"].value)
上面的代码通过form[key]
的方法访问了FieldStorage(或则MiniStorage,由表单的编码决定)相应的对象,除了像上面.value的方式获得相应的值之外,还可以通过form.getvalue(key)
的方法直接返回值。当表单中又多个相同名字的字段时,这两种方法都会返回一个值的列表,这时候他们是和form.getlist(key)
方法等效的。
如果某个字段代表一个上传的文件时,使用上面的getvalue等方法会以bytes的形式将整个文件读到内存中,这可能并不是你想要的结果。这个时候你可以尝试使用file
或者filename
属性在FieldStorage被垃圾回收自动关闭前获得文件(read()和readline()方法会返回bytes):
fileitem = form["userfile"]
if fileitem.file:
# It's an uploaded file; count lines
linecount = 0
while True:
line = fileitem.file.readline()
if not line: break
linecount = linecount + 1
如果上传文件在中途被中断了,该对象的done
属性将会被设为-1
。
3.高级接口
有时候你并不知道表单返回的数据是否会有多个相同名字的字段,传统的解决办法是在if语句里面判断返回的类型是string 还是 list,这样是有些繁琐的,cgi模块包含两个简单的方法来处理这种情况:
FieldStorage.getfirst(name, default=None)
则个方法只会返回name
对应的一个值,具体处理是这样的:
当只有一个字段对应时返回这个字段,当对应多个重名字段时,返回第一个(值得注意的是,这里的’第一个’是随着浏览器的不同而变化的),如果没有对应的字段,将会返回default对应的值,在这里是返回一个None。
FieldStorage.getlist(name)
这个方法任何情况下都会返回一个列表,当没有对应的字段时,返回的列表是空列表。
更多函数请查看官方文档。