Получить необработанное тело POST в Python Flask независимо от заголовка Content-Type

131

Ранее я спрашивал, как получить данные, полученные в запросе 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
ddinchev
источник

Ответы:

218

Используется 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будет казаться пустым в этом случае.

miracle2k
источник
2
Это похоже на сбой при использовании raven-python (Sentry), ошибки и обходные пути здесь: github.com/getsentry/raven-python/issues/457
dequis
34

request.streamпоток необработанных данных, передаваемых приложению сервером WSGI. При его чтении не выполняется синтаксический анализ, хотя обычно вы request.get_data()этого хотите .

data = request.stream.read()

Поток будет пустым, если он был ранее прочитан request.dataили другим атрибутом.

юлианский день.
источник
15

Я создал промежуточное программное обеспечение 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']
jhaski
источник
6

request.dataбудет пустым, если request.headers["Content-Type"]будет распознан как данные формы, которые будут проанализированы request.form. Чтобы получить необработанные данные независимо от типа контента, используйте request.get_data().

request.dataвызовы request.get_data(parse_form_data=True), что приводит к другому поведению данных формы.

KevinH
источник