将老旧Python Web应用改造为WSGI适配并部署于Apache环境
老旧Python CGI Web应用适配WSGI改造方案
问题背景
我有一个由Python编写的老旧Web应用,由多个.py文件组成,核心文件display.py示例代码如下:
import cgi import re import string import operator from urllib.parse import urlparse from errors import herigean from routines import * error = False query = cgiFieldStorageToDict(cgi.FieldStorage()) opening_index = 0 # flag to indicate whether we're opening the index page if ('what' not in query): query['what'] = 'index' if 'fs' not in query: query['fs'] = str(default_font_size) # open page to display try: fil = open('html/'+query['what']+'.fmt') textlines = fil.read() queryreg = re.compile('QUERY:fs:QUERY') textlines = queryreg.sub(query['fs'],textlines) fil.close() except IOError: error = True if query['what'] == 'about': try: fil = open('legal/lgpl-3.0.txt') lgpl = fil.read() fil.close() fil = open('legal/gpl.txt') gpl = fil.read() fil.close() fil = open('html/availability.fmt') availability = fil.read() fil.close() except IOError: error = True if query['what'] == 'corpus': try: fil = open('html/availability.fmt') [...] if error: herigean() else: print(frontmatter)
当前已在Python 3.11虚拟环境中安装mod_wsgi-express 5,能成功运行测试WSGI程序:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'Hello, world!']
遇到的具体问题:
- 不清楚如何改造原有应用适配WSGI,是否需要给每个.py文件都套上
def application(environ, start_response):? - 尝试将代码包裹在application函数中时,用
return print(frontmatter)导致日志输出完整HTML,但抛出TypeError: 'NoneType' object is not iterable错误,浏览器显示500内部服务器错误。 - 应用通过www目录下的
index.html跳转启动:<meta http-equiv="Refresh" content="0;url=display.py?what=index" />
改造解决方案
1. 无需给每个.py文件都套application函数
原有CGI应用的入口是display.py,只需要改造这个核心入口文件即可,其他依赖的.py文件(比如errors.py、routines.py)保持原样,只要确保WSGI环境能正确导入它们。
2. 替换CGI请求参数解析逻辑
原有代码用cgi.FieldStorage()获取请求参数,在WSGI环境下需要改用environ变量解析:
from urllib.parse import parse_qs def get_query_params(environ): query_string = environ.get('QUERY_STRING', '') params = parse_qs(query_string) # 将多值参数转为单值,适配原有逻辑 return {k: v[0] for k, v in params.items()}
替换原有的query = cgiFieldStorageToDict(cgi.FieldStorage())为query = get_query_params(environ)。
3. 修复return语句的错误
print(frontmatter)会输出内容到标准输出,但WSGI要求返回字节数组组成的可迭代对象,且print()函数返回None,这就是报错的核心原因。需要:
- 去掉
print()调用,直接返回编码后的字节内容 - 将HTML字符串转为字节数组(比如
frontmatter.encode('utf-8'))
4. 改造错误处理函数herigean()
原有的herigean()应该是直接输出错误页面到标准输出,需要修改它返回错误页面的字符串内容,然后在WSGI逻辑中正确调用:
# 假设修改后的herigean()返回错误页面字符串 if error: error_content = herigean() start_response('500 Internal Server Error', [('Content-Type', 'text/html')]) return [error_content.encode('utf-8')]
5. 完整改造后的display.py示例
import re import string import operator from urllib.parse import urlparse, parse_qs from errors import herigean from routines import * def get_query_params(environ): query_string = environ.get('QUERY_STRING', '') params = parse_qs(query_string) return {k: v[0] for k, v in params.items()} def application(environ, start_response): error = False query = get_query_params(environ) opening_index = 0 if 'what' not in query: query['what'] = 'index' if 'fs' not in query: query['fs'] = str(default_font_size) # 读取页面内容逻辑保持不变,改用with语句更安全 try: with open(f'html/{query["what"]}.fmt', 'r') as fil: textlines = fil.read() queryreg = re.compile('QUERY:fs:QUERY') textlines = queryreg.sub(query['fs'], textlines) except IOError: error = True if query['what'] == 'about': try: with open('legal/lgpl-3.0.txt', 'r') as fil: lgpl = fil.read() with open('legal/gpl.txt', 'r') as fil: gpl = fil.read() with open('html/availability.fmt', 'r') as fil: availability = fil.read() except IOError: error = True if query['what'] == 'corpus': try: with open('html/availability.fmt', 'r') as fil: # 补全原有业务逻辑 pass except IOError: error = True if error: error_content = herigean() start_response('500 Internal Server Error', [('Content-Type', 'text/html')]) return [error_content.encode('utf-8')] else: start_response('200 OK', [('Content-Type', 'text/html')]) return [frontmatter.encode('utf-8')]
6. 调整跳转逻辑
原index.html的跳转指向display.py,在WSGI部署后,需要修改跳转地址为WSGI应用的挂载路径,比如如果应用挂载在根路径,修改为:
<meta http-equiv="Refresh" content="0;url=/what=index" />
或保持原有参数格式:
<meta http-equiv="Refresh" content="0;url=/display?what=index" />
具体路径取决于mod_wsgi的配置。
内容的提问来源于stack exchange,提问作者HBMCS
相关产品推荐
相关产品推荐

