50、Python Web编程:CGI与WSGI的深入解析

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安全序列,如 &amp; &lt; &gt; 。若 quote True ,双引号字符 " 也会转换为 &quot;
- 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应用的开发质量和效率。

【电动汽车充电站有序充电调度的分散式优化】基于蒙特卡诺和拉格朗日的电动汽车优化调度(分时电价调度)(Matlab代码实现)内容概要:本文介绍了基于蒙特卡洛和拉格朗日方法的电动汽车充电站有序充电调度优化方案,重点在于采用分散式优化策略应对分时电价机制下的充电需求管理。通过构建数学模型,结合不确定性因素如用户充电行为和电网负荷波动,利用蒙特卡洛模拟生成大量场景,并运用拉格朗日松弛法对复杂问题进行分解求解,从而实现全局最优或近似最优的充电调度计划。该方法有效降低了电网峰值负荷压力,提升了充电站运营效率经济效益,同时兼顾用户充电便利性。 适合人群:具备一定电力系统、优化算法和Matlab编程基础的高校研究生、科研人员及从事智能电网、电动汽车相关领域的工程技术人员。 使用场景及目标:①应用于电动汽车充电站的日常运营管理,优化充电负荷分布;②服务于城市智能交通系统规划,提升电网交通系统的协同水平;③作为学术研究案例,用于验证分散式优化算法在复杂能源系统中的有效性。 阅读建议:建议读者结合Matlab代码实现部分,深入理解蒙特卡洛模拟拉格朗日松弛法的具体实施步骤,重点关注场景生成、约束处理迭代收敛过程,以便在实际项目中灵活应用改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值