无论Content-Type头是什么,如何在Python Flask中获取原始的POST body?

160

之前我在stackoverflow上问过一个问题(如何获取Flask请求中收到的数据),因为request.data是空的。答案解释说request.data是原始的POST主体,但如果表单数据被解析了,它就会为空。那么,我怎样才能无条件地获取原始的POST主体呢?

@app.route('/', methods=['POST'])
def parse_request():
    data = request.data  # empty in some cases
    # always need raw data here, not parsed form data
4个回答

265

使用request.get_data()方法,可以获取原始数据,无论内容类型如何。该数据被缓存,您随后可以随意访问request.datarequest.jsonrequest.form

如果先访问request.data,则会使用一个参数调用get_data以先解析表单数据。如果请求具有表单内容类型(multipart/form-dataapplication/x-www-form-urlencodedapplication/x-url-encoded),则原始数据将被消耗。在这种情况下,request.datarequest.json将为空。


45

request.stream 表示WSGI服务器传递给应用程序的原始数据流,读取它时不会进行解析,但通常你会想使用 request.get_data()

data = request.stream.read()

如果之前使用 request.data 或其他属性读取过该流,则该流将为空。


19

我创建了一个WSGI中间件,用于存储来自environ['wsgi.input']流的原始body。我在WSGI环境中保存了该值,以便能够从request.environ['body_copy']在我的应用程序中访问它。

在Werkzeug或Flask中这并不是必要的,因为request.get_data()将获取原始数据,无论内容类型如何,但具有更好的HTTP和WSGI行为处理能力。

这会将整个body读入内存,如果例如上传了大文件,则会出现问题。如果缺少Content-Length标头,则不会读取任何内容,因此它无法处理流请求。

from io import BytesIO

class WSGICopyBody(object):
    def __init__(self, application):
        self.application = application

    def __call__(self, environ, start_response):
        length = int(environ.get('CONTENT_LENGTH') or 0)
        body = environ['wsgi.input'].read(length)
        environ['body_copy'] = body
        # replace the stream since it was exhausted by read()
        environ['wsgi.input'] = BytesIO(body)
        return self.application(environ, start_response)

app.wsgi_app = WSGICopyBody(app.wsgi_app)
request.environ['body_copy']

13

request.data如果被识别为表单数据,则为空,这时将解析成request.form。要获取原始数据而不管内容类型,请使用request.get_data()

request.data调用request.get_data(parse_form_data=True),因此对于表单数据有不同的行为。


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