从WSGI访问POST数据

41

我似乎无法弄清楚如何使用WSGI访问POST数据。 我尝试了wsgi.org网站上的示例,但它不起作用。 我现在正在使用Python 3.0。 请不要推荐WSGI框架,因为这不是我想要的。

我想弄清楚如何将其转换为fieldstorage对象。


就目前而言,Python 3.0仍没有WSGI规范,因此您所做的任何事情都可能是徒劳的,因为最终的规范更新可能与任何人尝试实现Python 3.0所说的内容不兼容。对于WSGI应用程序,最好使用Python 2.X。 - Graham Dumpleton
7
不再需要这样做了:http://www.python.org/dev/peps/pep-3333/(让我们不要误导像我这样稍后阅读的人——也为他们节省时间) - jeromej
2
@JermoeJ - 他在2009年写下了这条评论,而你在2013年回复它;不要认为他想误导任何人。 :) - Sam
5个回答

31

假设您正在尝试将POST数据仅传递到FieldStorage对象中:

# env is the environment handed to you by the WSGI server.
# I am removing the query string from the env before passing it to the
# FieldStorage so we only have POST data in there.
post_env = env.copy()
post_env['QUERY_STRING'] = ''
post = cgi.FieldStorage(
    fp=env['wsgi.input'],
    environ=post_env,
    keep_blank_values=True
)

这在Python 3.0中不起作用 - 它在wsgi.input返回字节而不是字符串方面存在问题。 :( 我需要一种在Python 3.0中实现此功能的方法... - Evan Fosmark
你使用了哪个WSGI处理程序?如果我使用内置的CGIHandler,对我来说它可以正常工作。我在本地服务器上有一个名为“post.cgi”的文件,其内容在http://pastebin.com/f40849562上运行得很好。 - Mike Boers
wsgi.input是哪个io类?如果它是BufferedIOBase,那么您应该能够将其包装在TextIOWrapper中,以便cgi.FieldStorage可以使用它。 - Mike Boers
@Mike,我也考虑过这个问题,但是这样做会导致长期无法正常运行,因为发布的数据可以是二进制的(例如文件)。 - Evan Fosmark
@Evan:是特定的处理程序,还是CGIHandler?因为对我来说,使用3.0版本的CGIHandler工作得非常好。也许我没有很强烈地推动它? - Mike Boers
显示剩余4条评论

24
body= ''  # b'' for consistency on Python 3.0
try:
    length= int(environ.get('CONTENT_LENGTH', '0'))
except ValueError:
    length= 0
if length!=0:
    body= environ['wsgi.input'].read(length)

请注意,对于Python 3.0而言,WSGI尚未完全规范化,许多流行的WSGI基础设施尚未转换(或已经2to3d,但未经过适当的测试)。即使是wsgiref.simple_server也无法运行。如果今天要进行WSGI,则可能会遇到一些困难。


是的,我在使用wsgiref时遇到了问题。最终我实施了这个补丁。 - Evan Fosmark

6
这个方法对我有用(在Python 3.0中):
import urllib.parse

post_input = urllib.parse.parse_qs(environ['wsgi.input'].readline().decode(),True)

2

我曾经遇到过同样的问题,花费了一些时间研究解决方案。
完整的答案包含详细的资源(因为在python3上这里接受的解决方案对我来说不起作用,需要在env库中进行许多错误更正):

# the code below is taken from and explained officially here:
# https://wsgi.readthedocs.io/en/latest/specifications/handling_post_forms.html
import cgi
def is_post_request(environ):
    if environ['REQUEST_METHOD'].upper() != 'POST':
        return False
    content_type = environ.get('CONTENT_TYPE', 'application/x-www-form-urlencoded')
    return (content_type.startswith('application/x-www-form-urlencoded' or content_type.startswith('multipart/form-data')))
def get_post_form(environ):
    assert is_post_request(environ)
    input = environ['wsgi.input']
    post_form = environ.get('wsgi.post_form')
    if (post_form is not None
        and post_form[0] is input):
        return post_form[2]
    # This must be done to avoid a bug in cgi.FieldStorage
    environ.setdefault('QUERY_STRING', '')
    fs = cgi.FieldStorage(fp=input,
                          environ=environ,
                          keep_blank_values=1)
    new_input = InputProcessed()
    post_form = (new_input, input, fs)
    environ['wsgi.post_form'] = post_form
    environ['wsgi.input'] = new_input
    return fs
class InputProcessed(object):
    def read(self, *args):
        raise EOFError('The wsgi.input stream has already been consumed')
    readline = readlines = __iter__ = read

# the basic and expected application function for wsgi
# get_post_form(environ) returns a FieldStorage object
# to access the values use the method .getvalue('the_key_name')
# this is explained officially here:
# https://docs.python.org/3/library/cgi.html
# if you don't know what are the keys, use .keys() method and loop through them
def application(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    user = get_post_form(environ).getvalue('user')
    password = get_post_form(environ).getvalue('password')
    output = 'user is: '+user+' and password is: '+password
    return [output.encode()]

-2

仍然无法在Python 3.0中运行,这正是我正在寻找的。不过还是谢谢。 - Evan Fosmark
2
@FireCrow 看看框架是个好主意。这并不是在建议使用框架的方式。 - jeromej
2
链接已经失效了。 - Soravux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接