Python Web编程:CGI与WSGI的深入解析
1. CGI编程概述
CGI(Common Gateway Interface)脚本通常由Web服务器执行,用于处理表单中的用户输入或生成动态内容。当提交与CGI脚本对应的请求时,Web服务器会将CGI程序作为子进程执行。CGI程序的输入来源有两个:
sys.stdin
和服务器设置的环境变量。
1.1 常见的Web服务器环境变量
| 变量名 | 描述 |
|---|---|
| AUTH_TYPE | 认证方法 |
| CONTENT_LENGTH |
sys.stdin
中传递的数据长度
|
| CONTENT_TYPE | 查询数据的类型 |
| DOCUMENT_ROOT | 文档根目录 |
| GATEWAY_INTERFACE | CGI修订字符串 |
| HTTP_ACCEPT | 客户端接受的MIME类型 |
| HTTP_COOKIE | Netscape持久cookie值 |
| HTTP_FROM | 客户端的电子邮件地址(通常禁用) |
| HTTP_REFERER | 引用URL |
| HTTP_USER_AGENT | 客户端浏览器 |
| PATH_INFO | 传递的额外路径信息 |
| PATH_TRANSLATED |
PATH_INFO
的转换版本
|
| QUERY_STRING | 查询字符串 |
| REMOTE_ADDR | 客户端的远程IP地址 |
| REMOTE_HOST | 客户端的远程主机名 |
| REMOTE_IDENT | 发出请求的用户 |
| REMOTE_USER | 认证的用户名 |
| REQUEST_METHOD | 请求方法(’GET’或’POST’) |
| SCRIPT_NAME | 程序的名称 |
| SERVER_NAME | 服务器主机名 |
| SERVER_PORT | 服务器端口号 |
| SERVER_PROTOCOL | 服务器协议 |
| SERVER_SOFTWARE | 服务器软件的名称和版本 |
1.2 CGI程序的输出
CGI程序的输出分为两部分:HTTP头和原始数据(通常是HTML),两者之间用空行分隔。以下是一个简单的HTTP头示例:
print 'Content-type: text/html\r' # HTML Output
print '\r' # Blank line (required!)
完整的输出示例如下:
import cgi
form = cgi.FieldStorage()
name = form.getvalue('name')
email = form.getvalue('email')
print 'Content-type: text/html\r'
print '\r'
print '<TITLE>My CGI Script</TITLE>'
print '<H1>Hello World!</H1>'
print 'You are %s (%s)' % (name, email)
1.3
FieldStorage
类
FieldStorage
类用于读取和解析环境变量或标准输入中传递的查询字符串,以获取表单内容。其构造函数如下:
FieldStorage([input [, headers [, outerboundary [, environ [, keep_blank_values [, strict_parsing]]]]]])
参数说明:
-
input
:指定在POST请求中读取表单数据的类文件对象,默认使用
sys.stdin
。
-
headers
和
outerboundary
:内部使用,不应提供。
-
environ
:用于读取CGI环境变量的字典。
-
keep_blank_values
:布尔标志,控制是否保留空白值,默认值为
False
。
-
strict_parsing
:布尔标志,若解析出现问题则引发异常,默认值为
False
。
FieldStorage
实例的使用类似于字典,例如:
form = cgi.FieldStorage()
name = form['name'].value
email = form['email'].value
1.4 实用函数
CGI脚本中常用的实用函数如下:
-
escape(s [, quote])
:将字符串
s
中的字符
&
、
<
和
>
转换为HTML安全序列,如
&
、
<
和
>
。若
quote
为
True
,双引号字符
"
也会转换为
"
。
-
parse_header(string)
:解析HTTP头字段(如
content-type
)后面的数据,将其拆分为主值和二级参数的字典,并以元组形式返回。
-
parse_multipart(fp, pdict)
:解析
multipart/form-data
类型的输入,常用于文件上传。
-
print_directory()
:将当前工作目录的名称格式化为HTML并打印输出。
-
print_environ()
:创建所有环境变量的HTML列表,用于调试。
-
print_environ_usage()
:打印更有用的环境变量的HTML列表,用于调试。
-
print_form(form)
:将表单数据格式化为HTML,
form
必须是
FieldStorage
的实例。
-
test()
:写入最小的HTTP头,并以HTML格式打印提供给脚本的所有信息,主要用于调试。
1.5 CGI编程建议
-
使用模板
:避免使用大量
print语句生成硬编码的HTML输出,可使用string.Template对象。示例代码如下:
import cgi
from string import Template
def error(message):
temp = Template(open("errormsg.html").read())
print 'Content-type: text/html\r'
print '\r'
print temp.substitute({'message': message})
form = cgi.FieldStorage()
name = form.getfirst('name')
email = form.getfirst('email')
if not name:
error("name not specified")
raise SystemExit
elif not email:
error("email not specified")
raise SystemExit
# Do various processing
confirmation = subscribe(name, email)
# Print the output page
values = {
'name': name,
'email': email,
'confirmation': confirmation
}
temp = Template(open("success.html").read())
print temp.substitute(values)
-
使用数据库
:若需要保存CGI脚本的数据,建议使用数据库,如
sqlite3或第三方MySQL模块。 - 考虑使用Web框架 :若编写大量CGI脚本并处理HTTP的底层细节,可考虑使用Web框架。
1.6 注意事项
-
安装CGI程序的过程因Web服务器类型而异,通常程序放在
cgi-bin目录,可能还需要额外配置。 -
在UNIX系统上,Python CGI程序可能需要设置执行权限,并在第一行添加
#!/usr/bin/env python。 -
为简化调试,可导入
cgitb模块:import cgitb; cgitb.enable()。 - 调用外部程序时,避免将客户端的任意字符串传递给shell,防止安全漏洞。
-
在UNIX系统上,不要给CGI程序设置
setuid模式。 -
避免使用
from cgi import *,以免引入不必要的命名空间。
2. cgitb模块
cgitb
模块提供了一个替代的异常处理程序,当发生未捕获的异常时,会显示详细报告,包含源代码、参数值和局部变量。该模块最初用于调试CGI脚本,也可用于任何应用程序。
2.1 函数说明
-
enable([display [, logdir [, context [, format]]]]):启用特殊异常处理。 -
display:标志,决定错误发生时是否显示信息,默认值为1。 -
logdir:指定错误报告写入文件的目录,而不是打印到标准输出。 -
context:整数,指定异常发生行周围显示的源代码行数。 -
format:指定输出格式,html表示HTML格式(默认),其他值表示纯文本格式。 -
handle([info]):使用enable()函数的默认设置处理异常。info是一个元组(exctype, excvalue, tb),通常通过sys.exc_info()获取。若省略info,则使用当前异常。
示例代码:
import cgitb
cgitb.enable()
3. WSGI概述
WSGI(Python Web Server Gateway Interface)是Web服务器和Web应用程序之间的标准化接口,旨在促进应用程序在不同Web服务器和框架之间的可移植性。
3.1 WSGI规范
WSGI应用程序实现为一个函数或可调用对象
webapp(environ, start_response)
,接受两个参数:
-
environ
:环境设置的字典,包含CGI脚本中使用的常见值以及WSGI特定值。
-
start_response
:可调用对象,用于启动响应,格式为
start_response(status, headers)
。
以下是一个简单的WSGI应用程序示例:
import cgi
def subscribe_app(environ, start_response):
fields = cgi.FieldStorage(environ['wsgi.input'], environ=environ)
name = fields.getvalue("name")
email = fields.getvalue("email")
status = "200 OK"
headers = [('Content-type', 'text/plain')]
start_response(status, headers)
response = [
'Hi %s. Thank you for subscribing.' % name,
'You should expect a response soon.'
]
return (line.encode('utf-8') for line in response)
3.2 wsgiref包
wsgiref
包是WSGI标准的参考实现,可用于测试、验证和简单部署。
3.2.1
wsgiref.simple_server
模块
该模块实现了一个简单的独立HTTP服务器,运行单个WSGI应用程序。包含两个有用的函数:
-
make_server(host, port, app)
:创建一个HTTP服务器,接受指定主机名和端口的连接。
app
是实现WSGI应用程序的函数或可调用对象。
-
demo_app(environ, start_response)
:一个完整的WSGI应用程序,返回包含“Hello World”消息的页面。
示例代码:
def my_app(environ, start_response):
start_response("200 OK", [('Content-type', 'text/plain')])
return ['Hello World']
if __name__ == '__main__':
from wsgiref.simple_server import make_server
serv = make_server('', 8080, my_app)
serv.serve_forever()
3.2.2
wsgiref.handlers
模块
该模块包含用于设置WSGI执行环境的处理程序对象,使应用程序可以在其他Web服务器中运行。
-
CGIHandler()
:创建一个在标准CGI环境中运行的WSGI处理程序对象。
-
BaseCGIHandler(stdin, stdout, stderr, environ [, multithread [, multiprocess]])
:在CGI环境中操作的WSGI处理程序,标准I/O流和环境变量可能以不同方式设置。
-
SimpleHandler(stdin, stdout, stderr, environ [, multithread [, multiprocess]])
:类似于
BaseCGIHandler
,但让底层应用程序直接访问
stdin
、
stdout
、
stderr
和
environ
。
所有处理程序都有一个
run(app)
方法,用于在处理程序中运行WSGI应用程序。示例代码:
#!/usr/bin/env python
def my_app(environ, start_response):
start_response("200 OK", [('Content-type', 'text/plain')])
return ['Hello World']
from wsgiref.handlers import CGIHandler
hand = CGIHandler()
hand.run(my_app)
3.2.3
wsgiref.validate
模块
该模块的
validator(app)
函数用于包装WSGI应用程序,确保应用程序和服务器按照标准运行。任何违反标准的情况都会引发
AssertionError
异常。
示例代码:
def my_app(environ, start_response):
start_response("200 OK", [('Content-type', 'text/plain')])
return ['Hello World']
if __name__ == '__main__':
from wsgiref.simple_server import make_server
from wsgiref.validate import validator
serv = make_server('', 8080, validator(my_app))
serv.serve_forever()
3.3 总结
本文详细介绍了Python中的CGI和WSGI编程,包括CGI脚本的输入输出、
FieldStorage
类的使用、CGI编程建议、
cgitb
模块的异常处理,以及WSGI的规范和
wsgiref
包的使用。通过这些知识,开发者可以更好地进行Web编程,处理用户输入和生成动态内容。
3.4 流程图
graph TD;
A[客户端请求] --> B[Web服务器];
B --> C{请求类型};
C -- CGI请求 --> D[执行CGI脚本];
C -- WSGI请求 --> E[执行WSGI应用程序];
D --> F[生成响应];
E --> F;
F --> G[返回响应给客户端];
3.5 表格总结
| 模块 | 功能 | 主要函数/类 |
|---|---|---|
cgi
| 实现CGI脚本 |
FieldStorage
、
escape
、
parse_header
等
|
cgitb
| 提供异常处理 |
enable
、
handle
|
wsgiref
| 实现WSGI标准 |
make_server
、
CGIHandler
、
validator
|
4. WSGI应用部署要点
要部署一个WSGI应用,需要将其注册到所使用的Web编程框架中。虽然不同框架的具体操作不同,但一般步骤如下:
1.
选择Web框架
:确定使用的Web框架,如Flask、Django等。
2.
编写WSGI应用
:根据WSGI规范编写应用函数,例如前面提到的
subscribe_app
或
my_app
。
3.
集成到框架
:按照框架的文档说明,将WSGI应用集成到框架中。这可能涉及配置文件的修改、路由的设置等。
4.1 不同框架的部署示例
以下分别给出Flask和Django框架部署WSGI应用的简单示例:
4.1.1 Flask框架
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
在Flask中,
app
对象本身就是一个WSGI应用。可以直接使用
app.run()
启动开发服务器,也可以将其部署到生产环境的WSGI服务器上。
4.1.2 Django框架
在Django中,项目的
wsgi.py
文件已经定义了WSGI应用。通常可以使用Gunicorn等WSGI服务器来运行Django项目。
gunicorn myproject.wsgi:application
这里
myproject
是Django项目的名称,
wsgi:application
指向
wsgi.py
文件中的
application
对象。
4.2 注意事项
-
字符编码
:WSGI应用要求返回编码后的字节串,在Python 3中需要特别注意。例如在前面的
subscribe_app示例中,使用line.encode('utf-8')将字符串转换为字节串。 -
错误处理
:在WSGI应用中,要确保对可能出现的异常进行适当处理,避免未捕获的异常导致服务器崩溃。可以结合
cgitb模块进行详细的错误报告。
5. CGI与WSGI的比较
| 特性 | CGI | WSGI |
|---|---|---|
| 可移植性 | 较差,依赖于Web服务器的配置 | 高,标准化接口,可在不同服务器和框架中使用 |
| 性能 | 每次请求都会创建新的进程,开销大 | 可以在同一个进程中处理多个请求,性能较好 |
| 复杂度 | 编程相对复杂,需要处理大量底层细节 | 相对简单,有标准的接口和参考实现 |
| 适用场景 | 简单的Web应用,对性能要求不高 | 大型、复杂的Web应用,需要高可移植性和性能 |
5.1 选择建议
- 如果是简单的Web应用,且对性能要求不高,或者需要与旧的Web服务器兼容,可以选择CGI。
- 如果是大型、复杂的Web应用,需要在不同的Web服务器和框架之间进行移植,或者对性能有较高要求,建议选择WSGI。
6. 实际应用案例
6.1 CGI应用案例
假设要创建一个简单的表单处理程序,用于收集用户的姓名和电子邮件地址,并返回确认信息。可以使用CGI实现如下:
import cgi
form = cgi.FieldStorage()
name = form.getvalue('name')
email = form.getvalue('email')
print 'Content-type: text/html\r'
print '\r'
print '<TITLE>Form Confirmation</TITLE>'
print '<H1>Thank You!</H1>'
print 'You entered: %s (%s)' % (name, email)
将上述代码保存为一个Python脚本,放置在Web服务器的
cgi-bin
目录下,并确保有执行权限。用户提交表单后,服务器会执行该脚本并返回确认信息。
6.2 WSGI应用案例
创建一个简单的WSGI应用,用于返回当前时间:
import time
def time_app(environ, start_response):
status = "200 OK"
headers = [('Content-type', 'text/plain')]
start_response(status, headers)
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
return [current_time.encode('utf-8')]
if __name__ == '__main__':
from wsgiref.simple_server import make_server
serv = make_server('', 8080, time_app)
serv.serve_forever()
运行上述代码后,访问
http://localhost:8080
即可看到当前时间。
7. 总结与展望
7.1 总结
本文全面介绍了Python中的CGI和WSGI编程。CGI是一种传统的Web编程方式,适用于简单的应用场景,但性能和可移植性较差。WSGI是一种标准化的接口,提高了应用程序的可移植性和性能,更适合大型、复杂的Web应用。通过学习
cgi
、
cgitb
和
wsgiref
等模块的使用,开发者可以根据具体需求选择合适的编程方式。
7.2 展望
随着Web技术的不断发展,WSGI的应用将越来越广泛。未来,可能会有更多的Web框架和服务器支持WSGI标准,进一步提高Web应用的开发效率和可维护性。同时,对于CGI这种传统技术,也可能会在一些特定的场景中继续发挥作用。
7.3 流程图
graph LR;
A[选择编程方式] --> B{需求类型};
B -- 简单应用 --> C[CGI编程];
B -- 复杂应用 --> D[WSGI编程];
C --> E[编写CGI脚本];
D --> F[编写WSGI应用];
E --> G[部署到Web服务器];
F --> G;
G --> H[处理用户请求];
通过以上内容,希望能帮助开发者更好地理解和应用Python中的CGI和WSGI编程技术,根据实际需求选择合适的方案,提高Web应用的开发质量和效率。
超级会员免费看
90

被折叠的 条评论
为什么被折叠?



