以前,我问过 How to get data received in Flask request,因为 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
无论内容类型如何,都使用 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
将显示为空。
request.stream
是 WSGI 服务器传递给应用程序的原始数据流。读取时不进行解析,尽管您通常需要 request.get_data()
。
data = request.stream.read()
如果之前由 request.data
或其他属性读取,则该流将为空。
我创建了一个 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']
如果 request.headers["Content-Type"]
被识别为表单数据,则 request.data
将为空,并将其解析为 request.form
。要获取原始数据而不考虑内容类型,请使用 request.get_data()
。
request.data
调用 request.get_data(parse_form_data=True)
,从而导致表单数据的不同行为。
不定期副业成功案例分享