问题:无论内容类型标头如何,都可以在Python Flask中获取原始POST正文
以前,我问过如何获取Flask请求中的数据,因为它request.data
是空的。答案解释request.data
为原始帖子正文,但如果分析表单数据将为空。我如何无条件获得原始职位?
@app.route('/', methods=['POST'])
def parse_request():
data = request.data # empty in some cases
# always need raw data here, not parsed form data
回答 0
使用request.get_data()
获得的原始数据,而不管内容类型。该数据被缓存,您可以随后访问request.data
,request.json
,request.form
随意。
如果您request.data
首先访问,它将首先调用get_data
一个参数以解析表单数据。如果请求具有形式的内容类型(multipart/form-data
,application/x-www-form-urlencoded
,或application/x-url-encoded
),则原始数据将被消耗。request.data
并且request.json
在这种情况下将显示为空。
回答 1
request.stream
是WSGI服务器传递给应用程序的原始数据流。读取时不进行任何解析,尽管通常会这样做request.get_data()
。
data = request.stream.read()
如果该流先前被request.data
或其他属性读取,则将为空。
回答 2
我创建了一个WSGI中间件,用于存储environ['wsgi.input']
流中的原始内容。我将值保存在WSGI环境中,因此可以从request.environ['body_copy']
我的应用程序中访问它。
在Werkzeug或Flask中这不是必需的,因为request.get_data()
无论内容类型如何,都将获取原始数据,但是可以更好地处理HTTP和WSGI行为。
这会将整个主体读入内存,如果发布了一个大文件,这将是一个问题。如果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']
回答 3
request.data
如果request.headers["Content-Type"]
被识别为表单数据,则将为空,并将其解析为request.form
。要获取原始数据而不管内容类型如何,请使用request.get_data()
。
request.data
调用request.get_data(parse_form_data=True)
,这导致表单数据的行为不同。