cgiCommon Gateway Interface support通用网关接口支持

Source code: Lib/cgi.py

Deprecated since version 3.11: 自3.11版起已弃用:The cgi module is deprecated (see PEP 594 for details).cgi模块已弃用(有关详细信息,请参阅PEP 594)。


Support module for Common Gateway Interface (CGI) scripts.通用网关接口(CGI)脚本支持模块。

This module defines a number of utilities for use by CGI scripts written in Python.该模块定义了许多实用程序,供用Python编写的CGI脚本使用。

Introduction介绍

A CGI script is invoked by an HTTP server, usually to process user input submitted through an HTML <FORM> or <ISINDEX> element.CGI脚本由HTTP服务器调用,通常用于处理通过HTML<FORM><ISINDEX>元素提交的用户输入。

Most often, CGI scripts live in the server’s special cgi-bin directory. 通常,CGI脚本位于服务器的特殊cgi-bin目录中。The HTTP server places all sorts of information about the request (such as the client’s hostname, the requested URL, the query string, and lots of other goodies) in the script’s shell environment, executes the script, and sends the script’s output back to the client.HTTP服务器将有关请求的各种信息(例如客户端的主机名、请求的URL、查询字符串和许多其他有用信息)放在脚本的shell环境中,执行脚本,并将脚本的输出发送回客户端。

The script’s input is connected to the client too, and sometimes the form data is read this way; at other times the form data is passed via the “query string” part of the URL. 脚本的输入也连接到客户端,有时表单数据是这样读取的;在其他时间,表单数据通过URL的“查询字符串”部分传递。This module is intended to take care of the different cases and provide a simpler interface to the Python script. 本模块旨在处理不同的情况,并为Python脚本提供更简单的接口。It also provides a number of utilities that help in debugging scripts, and the latest addition is support for file uploads from a form (if your browser supports it).它还提供了许多帮助调试脚本的实用程序,最新添加的是支持从表单上载文件(如果您的浏览器支持)。

The output of a CGI script should consist of two sections, separated by a blank line. CGI脚本的输出应该由两个部分组成,用空白行分隔。The first section contains a number of headers, telling the client what kind of data is following. Python code to generate a minimal header section looks like this:第一部分包含许多标头,告诉客户端后面是什么样的数据。生成最小标题部分的Python代码如下所示:

print("Content-Type: text/html")    # HTML is following
print() # blank line, end of headers

The second section is usually HTML, which allows the client software to display nicely formatted text with header, in-line images, etc. Here’s Python code that prints a simple piece of HTML:第二部分通常是HTML,它允许客户端软件显示具有标题、内嵌图像等格式的文本。下面是打印一段简单HTML的Python代码:

print("<TITLE>CGI script output</TITLE>")
print("<H1>This is my first CGI script</H1>")
print("Hello, world!")

Using the cgi module使用cgi模块

Begin by writing import cgi.首先编写import cgi

When you write a new script, consider adding these lines:编写新脚本时,请考虑添加以下行:

import cgitb
cgitb.enable()

This activates a special exception handler that will display detailed reports in the web browser if any errors occur. 这将激活一个特殊的异常处理程序,如果发生任何错误,该处理程序将在web浏览器中显示详细报告。If you’d rather not show the guts of your program to users of your script, you can have the reports saved to files instead, with code like this:如果您不想向脚本用户显示程序的内部内容,可以将报告保存到文件中,代码如下:

import cgitb
cgitb.enable(display=0, logdir="/path/to/logdir")

It’s very helpful to use this feature during script development. 在脚本开发期间使用此功能非常有用。The reports produced by cgitb provide information that can save you a lot of time in tracking down bugs. cgitb生成的报告提供了信息,可以为您节省大量时间来跟踪错误。You can always remove the cgitb line later when you have tested your script and are confident that it works correctly.当您测试了脚本并确信它正常工作时,您可以稍后删除cgitb行。

To get at submitted form data, use the FieldStorage class. 要获取提交的表单数据,请使用FieldStorage类。If the form contains non-ASCII characters, use the encoding keyword parameter set to the value of the encoding defined for the document. 如果表单包含非ASCII字符,请使用设置为文档定义的编码值的encoding关键字参数。It is usually contained in the META tag in the HEAD section of the HTML document or by the Content-Type header. 它通常包含在HTML文档HEAD部分的META标记中,或由Content-Type标头包含。This reads the form contents from the standard input or the environment (depending on the value of various environment variables set according to the CGI standard). 这将从标准输入或环境中读取表单内容(取决于根据CGI标准设置的各种环境变量的值)。Since it may consume standard input, it should be instantiated only once.由于它可能消耗标准输入,因此应该只实例化一次。

The FieldStorage instance can be indexed like a Python dictionary. FieldStorage实例可以像Python字典一样进行索引。It allows membership testing with the in operator, and also supports the standard dictionary method keys() and the built-in function len(). 它允许使用in运算符进行成员资格测试,还支持标准字典方法keys()和内置函数len()Form fields containing empty strings are ignored and do not appear in the dictionary; to keep such values, provide a true value for the optional keep_blank_values keyword parameter when creating the FieldStorage instance.包含空字符串的表单字段将被忽略,并且不会出现在字典中;要保留这些值,请在创建FieldStorage实例时为可选的keep_blank_values关键字参数提供一个真值。

For instance, the following code (which assumes that the Content-Type header and blank line have already been printed) checks that the fields name and addr are both set to a non-empty string:例如,以下代码(假设Content-Type标头和空行已经打印)检查字段nameaddr是否都设置为非空字符串:

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)
...further form processing here...

Here the fields, accessed through form[key], are themselves instances of FieldStorage (or MiniFieldStorage, depending on the form encoding). 这里,通过form[key]访问的字段本身就是FieldStorage(或MiniFieldStorage,取决于表单编码)的实例。The value attribute of the instance yields the string value of the field. 实例的value属性产生字段的字符串值。The getvalue() method returns this string value directly; it also accepts an optional second argument as a default to return if the requested key is not present.getvalue()方法直接返回此字符串值;如果请求的键不存在,它还接受可选的第二个参数作为默认值返回。

If the submitted form data contains more than one field with the same name, the object retrieved by form[key] is not a FieldStorage or MiniFieldStorage instance but a list of such instances. 如果提交的表单数据包含多个同名字段,则form[key]检索的对象不是FieldStorageMiniFieldStorage实例,而是此类实例的列表。Similarly, in this situation, form.getvalue(key) would return a list of strings. 类似地,在这种情况下,form.getvalue(key)将返回字符串列表。If you expect this possibility (when your HTML form contains multiple fields with the same name), use the getlist() method, which always returns a list of values (so that you do not need to special-case the single item case). 如果您希望有这种可能性(当HTML表单包含多个同名字段时),请使用getlist()方法,它总是返回一个值列表(这样您就不需要对单个项目进行特殊区分)。For example, this code concatenates any number of username fields, separated by commas:例如,此代码连接任意数量的用户名字段,用逗号分隔:

value = form.getlist("username")
usernames = ",".join(value)

If a field represents an uploaded file, accessing the value via the value attribute or the getvalue() method reads the entire file in memory as bytes. 如果一个字段表示一个上传的文件,那么通过value属性或getvalue()方法访问该值将以字节形式读取内存中的整个文件。This may not be what you want. 这可能不是你想要的。You can test for an uploaded file by testing either the filename attribute or the file attribute. 您可以通过测试filename属性或file属性来测试上载的文件。You can then read the data from the file attribute before it is automatically closed as part of the garbage collection of the FieldStorage instance (the read() and readline() methods will return bytes):然后,您可以在file属性作为FieldStorage实例的垃圾收集的一部分自动关闭之前从文件属性读取数据(read()readline()方法将返回字节):

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

FieldStorage objects also support being used in a with statement, which will automatically close them when done.FieldStorage对象还支持在with语句中使用,完成后将自动关闭它们。

If an error is encountered when obtaining the contents of an uploaded file (for example, when the user interrupts the form submission by clicking on a Back or Cancel button) the done attribute of the object for the field will be set to the value -1.如果在获取上载文件的内容时遇到错误(例如,当用户通过单击“后退”或“取消”按钮中断表单提交时),则字段对象的done属性将设置为值-1。

The file upload draft standard entertains the possibility of uploading multiple files from one field (using a recursive multipart/* encoding). 文件上传草案标准允许从一个字段上传多个文件(使用递归multipart/*编码)。When this occurs, the item will be a dictionary-like FieldStorage item. 发生这种情况时,该项将是类似于FieldStorage的字典项。This can be determined by testing its type attribute, which should be multipart/form-data (or perhaps another MIME type matching multipart/*). 这可以通过测试其type属性来确定,该属性应该是multipart/form数据(或者可能是匹配multipart/*的其他MIME类型)。In this case, it can be iterated over recursively just like the top-level form object.在这种情况下,它可以像顶级表单对象一样递归迭代。

When a form is submitted in the “old” format (as the query string or as a single data part of type application/x-www-form-urlencoded), the items will actually be instances of the class MiniFieldStorage. 当表单以“旧”格式提交时(作为查询字符串或作为application/x-www-form-urlencoded类型的单个数据部分),这些项实际上将是MiniFieldStorage类的实例。In this case, the list, file, and filename attributes are always None.在这种情况下,listfilefilename属性始终为None

A form submitted via POST that also has a query string will contain both FieldStorage and MiniFieldStorage items.通过POST提交的表单(也包含查询字符串)将同时包含FieldStorageMiniFieldStorage项。

Changed in version 3.4:版本3.4中更改: The file attribute is automatically closed upon the garbage collection of the creating FieldStorage instance.file属性在创建FieldStorage实例的垃圾收集时自动关闭。

Changed in version 3.5:版本3.5中更改: Added support for the context management protocol to the FieldStorage class.FieldStorage类添加了对上下文管理协议的支持。

Higher Level Interface高级界面

The previous section explains how to read CGI form data using the FieldStorage class. 上一节解释了如何使用FieldStorage类读取CGI表单数据。This section describes a higher level interface which was added to this class to allow one to do it in a more readable and intuitive way. 本节描述了一个更高级别的接口,它被添加到这个类中,以允许用户以更可读和直观的方式进行操作。The interface doesn’t make the techniques described in previous sections obsolete — they are still useful to process file uploads efficiently, for example.该接口并没有使前几节中描述的技术过时——例如,它们对于有效地处理文件上传仍然有用。

The interface consists of two simple methods. 接口由两个简单的方法组成。Using the methods you can process form data in a generic way, without the need to worry whether only one or more values were posted under one name.使用这些方法,您可以以通用的方式处理表单数据,而无需担心是否在一个名称下只发布了一个或多个值。

In the previous section, you learned to write following code anytime you expected a user to post more than one value under one name:在上一节中,您学习了在期望用户在一个名称下发布多个值时编写以下代码:

item = form.getvalue("item")
if isinstance(item, list):
# The user is requesting more than one item.
else:
# The user is requesting only one item.

This situation is common for example when a form contains a group of multiple checkboxes with the same name:例如,当表单包含一组具有相同名称的多个复选框时,这种情况很常见:

<input type="checkbox" name="item" value="1" />
<input type="checkbox" name="item" value="2" />

In most situations, however, there’s only one form control with a particular name in a form and then you expect and need only one value associated with this name. 然而,在大多数情况下,表单中只有一个具有特定名称的表单控件,因此您只需要一个与该名称关联的值。So you write a script containing for example this code:因此,您编写了一个脚本,例如包含以下代码:

user = form.getvalue("user").upper()

The problem with the code is that you should never expect that a client will provide valid input to your scripts. 代码的问题在于,您永远不应该期望客户端向您的脚本提供有效的输入。For example, if a curious user appends another user=foo pair to the query string, then the script would crash, because in this situation the getvalue("user") method call returns a list instead of a string. 例如,如果一个好奇的用户将另一个user=foo对附加到查询字符串,那么脚本就会崩溃,因为在这种情况下,getvalue("user")方法调用返回的是列表而不是字符串。Calling the upper() method on a list is not valid (since lists do not have a method of this name) and results in an AttributeError exception.对列表调用upper()方法无效(因为列表没有此名称的方法),并导致AttributeError异常。

Therefore, the appropriate way to read form data values was to always use the code which checks whether the obtained value is a single value or a list of values. 因此,读取表单数据值的适当方法是始终使用代码来检查获得的值是单个值还是值列表。That’s annoying and leads to less readable scripts.这很烦人,导致脚本可读性降低。

A more convenient approach is to use the methods getfirst() and getlist() provided by this higher level interface.一种更方便的方法是使用此高级接口提供的方法getfirst()getlist()

FieldStorage.getfirst(name, default=None)

This method always returns only one value associated with form field name. 此方法始终只返回一个与表单name名关联的值。The method returns only the first value in case that more values were posted under such name. 如果在该名称下发布了更多值,则该方法只返回第一个值。Please note that the order in which the values are received may vary from browser to browser and should not be counted on. 请注意,接收值的顺序可能因浏览器而异,不应计入。1 If no such form field or value exists then the method returns the value specified by the optional parameter default. 如果不存在这样的表单字段或值,则该方法返回可选参数default指定的值。This parameter defaults to None if not specified.如果未指定,此参数默认为None

FieldStorage.getlist(name)

This method always returns a list of values associated with form field name. 此方法始终返回与表单字段name关联的值列表。The method returns an empty list if no such form field or value exists for name. 如果name不存在这样的表单字段或值,则该方法返回一个空列表。It returns a list consisting of one item if only one such value exists.如果只有一个这样的值存在,它将返回一个包含一个项的列表。

Using these methods you can write nice compact code:使用这些方法,您可以编写漂亮的压缩代码:

import cgi
form = cgi.FieldStorage()
user = form.getfirst("user", "").upper() # This way it's safe.
for item in form.getlist("item"):
do_something(item)

Functions功能

These are useful if you want more control, or if you want to employ some of the algorithms implemented in this module in other circumstances.如果你想要更多的控制,或者你想在其他情况下使用本模块中实现的一些算法,这些都很有用。

cgi.parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator='&')

Parse a query in the environment or from a file (the file defaults to sys.stdin). 分析环境中或文件中的查询(文件默认为sys.stdin)。The keep_blank_values, strict_parsing and separator parameters are passed to urllib.parse.parse_qs() unchanged.keep_blank_valuesstrict_parsingseparator参数不变地传递给urllib.parse.parse_qs()

cgi.parse_multipart(fp, pdict, encoding='utf-8', errors='replace', separator='&')

Parse input of type multipart/form-data (for file uploads). 分析multipart/form-data类型的输入(用于文件上载)。Arguments are fp for the input file, pdict for a dictionary containing other parameters in the Content-Type header, and encoding, the request encoding.参数为fp表示输入文件,pdict表示包含Content-Type标头中其他参数的字典,以及encoding表示请求编码。

Returns a dictionary just like urllib.parse.parse_qs(): keys are the field names, each value is a list of values for that field. 返回一个字典,就像urllib.parse.parse_qs():键是字段名,每个值是该字段的值列表。For non-file fields, the value is a list of strings.对于非文件字段,值是字符串列表。

This is easy to use but not much good if you are expecting megabytes to be uploaded — in that case, use the FieldStorage class instead which is much more flexible.这很容易使用,但如果您希望上传兆字节,就不太好了——在这种情况下,请使用FieldStorage类,这样更灵活。

Changed in version 3.7:版本3.7中更改: Added the encoding and errors parameters. 添加了encodingerrors参数。For non-file fields, the value is now a list of strings, not bytes.对于非文件字段,该值现在是字符串列表,而不是字节。

Changed in version 3.10:版本3.10中更改: Added the separator parameter.添加了separator参数。

cgi.parse_header(string)

Parse a MIME header (such as Content-Type) into a main value and a dictionary of parameters.将MIME标头(如Content-Type)解析为主值和参数字典。

cgi.test()

Robust test CGI script, usable as main program. Writes minimal HTTP headers and formats all information provided to the script in HTML format.强大的测试CGI脚本,可用作主程序。写入最少的HTTP标头,并将提供给脚本的所有信息格式化为HTML格式。

cgi.print_environ()

Format the shell environment in HTML.用HTML格式化shell环境。

cgi.print_form(form)

Format a form in HTML.以HTML格式设置表单格式。

cgi.print_directory()

Format the current directory in HTML.用HTML格式化当前目录。

cgi.print_environ_usage()

Print a list of useful (used by CGI) environment variables in HTML.以HTML格式打印有用的(CGI使用的)环境变量列表。

Caring about security关心安全

There’s one important rule: if you invoke an external program (via os.system(), os.popen() or other functions with similar functionality), make very sure you don’t pass arbitrary strings received from the client to the shell. 有一条重要的规则:如果您调用外部程序(通过os.system()os.popen()或其他具有类似功能的函数),请确保不要将从客户端接收的任意字符串传递给shell。This is a well-known security hole whereby clever hackers anywhere on the web can exploit a gullible CGI script to invoke arbitrary shell commands. 这是一个众所周知的安全漏洞,网络上任何地方的聪明黑客都可以利用一个容易受骗的CGI脚本来调用任意shell命令。Even parts of the URL or field names cannot be trusted, since the request doesn’t have to come from your form!即使是URL或域名的一部分也不可信,因为请求不必来自您的表单!

To be on the safe side, if you must pass a string gotten from a form to a shell command, you should make sure the string contains only alphanumeric characters, dashes, underscores, and periods.为了安全起见,如果必须将从表单获取的字符串传递给shell命令,则应确保该字符串仅包含字母数字字符、短划线、下划线和句点。

Installing your CGI script on a Unix system在Unix系统上安装CGI脚本

Read the documentation for your HTTP server and check with your local system administrator to find the directory where CGI scripts should be installed; usually this is in a directory cgi-bin in the server tree.阅读HTTP服务器的文档,并与本地系统管理员联系,查找应该安装CGI脚本的目录;通常这位于服务器树中的cgi-bin目录中。

Make sure that your script is readable and executable by “others”; the Unix file mode should be 0o755 octal (use chmod 0755 filename). 确保您的脚本可读并可由“其他人”执行;Unix文件模式应为0o755八进制(使用chmod 0755 filename)。Make sure that the first line of the script contains #! starting in column 1 followed by the pathname of the Python interpreter, for instance:确保脚本的第一行包含#!从第1列开始,后跟Python解释器的路径名,例如:

#!/usr/local/bin/python

Make sure the Python interpreter exists and is executable by “others”.确保Python解释器存在并且可由“其他人”执行。

Make sure that any files your script needs to read or write are readable or writable, respectively, by “others” — their mode should be 0o644 for readable and 0o666 for writable. 确保脚本需要读取或写入的任何文件都可以分别由“其他”读取或写入-其模式应为0o644(可读)和0o666(可写)。This is because, for security reasons, the HTTP server executes your script as user “nobody”, without any special privileges. 这是因为,出于安全原因,HTTP服务器以用户“nobody”的身份执行脚本,没有任何特殊权限。It can only read (write, execute) files that everybody can read (write, execute). 它只能读取(写入,执行)每个人都可以读取(写入、执行)的文件。The current directory at execution time is also different (it is usually the server’s cgi-bin directory) and the set of environment variables is also different from what you get when you log in. 执行时的当前目录也不同(它通常是服务器的cgi-bin目录),环境变量集也与登录时的不同。In particular, don’t count on the shell’s search path for executables (PATH) or the Python module search path (PYTHONPATH) to be set to anything interesting.特别是,不要指望shell的可执行文件搜索路径(PATH)或Python模块搜索路径(PYTHONPATH)设置为任何有趣的内容。

If you need to load modules from a directory which is not on Python’s default module search path, you can change the path in your script, before importing other modules. 如果需要从不在Python默认模块搜索路径上的目录加载模块,可以在导入其他模块之前更改脚本中的路径。For example:例如:

import sys
sys.path.insert(0, "/usr/home/joe/lib/python")
sys.path.insert(0, "/usr/local/lib/python")

(This way, the directory inserted last will be searched first!)(这样,将首先搜索最后插入的目录!)

Instructions for non-Unix systems will vary; check your HTTP server’s documentation (it will usually have a section on CGI scripts).非Unix系统的说明会有所不同;检查HTTP服务器的文档(通常会有一节关于CGI脚本)。

Testing your CGI script测试CGI脚本

Unfortunately, a CGI script will generally not run when you try it from the command line, and a script that works perfectly from the command line may fail mysteriously when run from the server. 不幸的是,当您从命令行尝试时,CGI脚本通常不会运行,而从命令行完美运行的脚本在从服务器运行时可能会神秘地失败。There’s one reason why you should still test your script from the command line: if it contains a syntax error, the Python interpreter won’t execute it at all, and the HTTP server will most likely send a cryptic error to the client.仍然应该从命令行测试脚本的原因有一个:如果脚本包含语法错误,Python解释器根本不会执行它,HTTP服务器很可能会向客户端发送一个隐藏的错误。

Assuming your script has no syntax errors, yet it does not work, you have no choice but to read the next section.假设您的脚本没有语法错误,但它不起作用,您别无选择,只能阅读下一节。

Debugging CGI scripts调试CGI脚本

First of all, check for trivial installation errors — reading the section above on installing your CGI script carefully can save you a lot of time. 首先,检查是否存在微小的安装错误-仔细阅读上面关于安装CGI脚本的章节可以节省大量时间。If you wonder whether you have understood the installation procedure correctly, try installing a copy of this module file (cgi.py) as a CGI script. 如果您想知道是否正确理解了安装过程,请尝试将此模块文件(cg.py)的副本安装为CGI脚本。When invoked as a script, the file will dump its environment and the contents of the form in HTML format. 当作为脚本调用时,文件将以HTML格式转储其环境和表单内容。Give it the right mode etc., and send it a request. 给它正确的模式等,并向它发送请求。If it’s installed in the standard cgi-bin directory, it should be possible to send it a request by entering a URL into your browser of the form:如果它安装在标准cgi-bin目录中,应该可以通过在浏览器中输入URL向它发送请求,格式如下:

http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home

If this gives an error of type 404, the server cannot find the script – perhaps you need to install it in a different directory. 如果出现404类型的错误,则服务器找不到脚本,可能需要将其安装在其他目录中。If it gives another error, there’s an installation problem that you should fix before trying to go any further. 如果它给出了另一个错误,那么在尝试进一步操作之前,应该解决安装问题。If you get a nicely formatted listing of the environment and form content (in this example, the fields should be listed as “addr” with value “At Home” and “name” with value “Joe Blow”), the cgi.py script has been installed correctly. 如果您获得了一个格式良好的环境和表单内容列表(在本例中,字段应列为值为“At Home”的“addr”和值为“Joe Blow”的“name”),则cgi.py脚本已正确安装。If you follow the same procedure for your own script, you should now be able to debug it.如果对自己的脚本执行相同的过程,现在应该可以调试它了。

The next step could be to call the cgi module’s test() function from your script: replace its main code with the single statement下一步可能是从脚本中调用cgi模块的test()函数:用单个语句替换其主代码

cgi.test()

This should produce the same results as those gotten from installing the cgi.py file itself.这将产生与安装cgi.py文件本身相同的结果。

When an ordinary Python script raises an unhandled exception (for whatever reason: of a typo in a module name, a file that can’t be opened, etc.), the Python interpreter prints a nice traceback and exits. 当一个普通的Python脚本引发一个未处理的异常(无论是什么原因:模块名中的拼写错误、无法打开的文件等)时,Python解释器会打印一个漂亮的回溯并退出。While the Python interpreter will still do this when your CGI script raises an exception, most likely the traceback will end up in one of the HTTP server’s log files, or be discarded altogether.虽然当您的CGI脚本引发异常时,Python解释器仍会执行此操作,但最有可能的是,回溯将在HTTP服务器的一个日志文件中结束,或者被完全丢弃。

Fortunately, once you have managed to get your script to execute some code, you can easily send tracebacks to the web browser using the cgitb module. 幸运的是,一旦您成功地让脚本执行了some代码,就可以使用cgitb模块轻松地将回溯发送到web浏览器。If you haven’t done so already, just add the lines:如果您还没有这样做,只需添加以下行:

import cgitb
cgitb.enable()

to the top of your script. 到脚本的顶部。Then try running it again; when a problem occurs, you should see a detailed report that will likely make apparent the cause of the crash.然后尝试再次运行它;当出现问题时,您应该看到一份详细的报告,该报告很可能会清楚地说明崩溃的原因。

If you suspect that there may be a problem in importing the cgitb module, you can use an even more robust approach (which only uses built-in modules):如果您怀疑导入cgitb模块时可能存在问题,可以使用一种更稳健的方法(仅使用内置模块):

import sys
sys.stderr = sys.stdout
print("Content-Type: text/plain")
print()
...your code here...

This relies on the Python interpreter to print the traceback. 这依赖于Python解释器来打印回溯。The content type of the output is set to plain text, which disables all HTML processing. 输出的内容类型设置为纯文本,这将禁用所有HTML处理。If your script works, the raw HTML will be displayed by your client. 如果您的脚本有效,则客户端将显示原始HTML。If it raises an exception, most likely after the first two lines have been printed, a traceback will be displayed. 如果它引发异常,很可能是在打印了前两行之后,将显示回溯。Because no HTML interpretation is going on, the traceback will be readable.因为没有HTML解释,所以回溯将是可读的。

Common problems and solutions常见问题及解决方案

  • Most HTTP servers buffer the output from CGI scripts until the script is completed. 大多数HTTP服务器缓冲CGI脚本的输出,直到脚本完成。This means that it is not possible to display a progress report on the client’s display while the script is running.这意味着在脚本运行时,无法在客户端显示器上显示进度报告。

  • Check the installation instructions above.检查上述安装说明。

  • Check the HTTP server’s log files. (tail -f logfile in a separate window may be useful!)检查HTTP服务器的日志文件。(单独窗口中的tail -f logfile可能有用!)

  • Always check a script for syntax errors first, by doing something like python script.py.总是先检查脚本中的语法错误,比如python script.py

  • If your script does not have any syntax errors, try adding import cgitb; cgitb.enable() to the top of the script.如果脚本没有任何语法错误,请尝试添加import cgitb; cgitb.enable()添加到脚本顶部。

  • When invoking external programs, make sure they can be found. 调用外部程序时,确保可以找到它们。Usually, this means using absolute path names — PATH is usually not set to a very useful value in a CGI script.通常,这意味着使用绝对路径名-在CGI脚本中,PATH通常不会设置为非常有用的值。

  • When reading or writing external files, make sure they can be read or written by the userid under which your CGI script will be running: this is typically the userid under which the web server is running, or some explicitly specified userid for a web server’s suexec feature.在读取或写入外部文件时,请确保这些文件可以由运行CGI脚本的用户ID读取或写入:这通常是运行web服务器的用户ID,或者是为web服务器的suexec功能明确指定的用户ID。

  • Don’t try to give a CGI script a set-uid mode. 不要尝试给CGI脚本设置uid模式。This doesn’t work on most systems, and is a security liability as well.这在大多数系统上都不起作用,也是一种安全隐患。

Footnotes

1

Note that some recent versions of the HTML specification do state what order the field values should be supplied in, but knowing whether a request was received from a conforming browser, or even from a browser at all, is tedious and error-prone.请注意,HTML规范的一些最新版本确实规定了字段值应按什么顺序提供,但知道请求是从符合要求的浏览器接收的,甚至是从浏览器接收到的,这是乏味且容易出错的。