Obter corpo POST bruto no Python Flask, independentemente do cabeçalho Content-Type

131

Anteriormente, perguntei Como obter dados recebidos na solicitação do Flask porque request.dataestava vazio. A resposta explicou que request.dataé o corpo da postagem não processada, mas ficará vazio se os dados do formulário forem analisados. Como posso obter o corpo da postagem bruto incondicionalmente?

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

Respostas:

218

Use request.get_data()para obter os dados brutos, independentemente do tipo de conteúdo. Os dados são armazenados em cache e você pode, posteriormente, o acesso request.data, request.json, request.formà vontade.

Se você acessar request.dataprimeiro, ele chamará get_datacom um argumento para analisar os dados do formulário primeiro. Se o pedido tem um tipo de conteúdo forma ( multipart/form-data, application/x-www-form-urlencoded, ou application/x-url-encoded) em seguida, os dados em bruto vai ser consumida. request.datae request.jsonaparecerá vazio neste caso.

miracle2k
fonte
2
Este parece quebrar quando usando Raven-python (Sentry), erro e soluções alternativas aqui: github.com/getsentry/raven-python/issues/457
dequis
34

request.streamé o fluxo de dados brutos passados ​​para o aplicativo pelo servidor WSGI. Nenhuma análise é feita durante a leitura, embora você geralmente queira request.get_data().

data = request.stream.read()

O fluxo ficará vazio se tiver sido lido anteriormente por request.dataou por outro atributo.

jd.
fonte
15

Criei um middleware WSGI que armazena o corpo bruto do environ['wsgi.input']fluxo. Salvei o valor no ambiente WSGI para poder acessá-lo request.environ['body_copy']no meu aplicativo.

Isso não é necessário no Werkzeug ou no Flask, pois request.get_data()os dados brutos serão obtidos independentemente do tipo de conteúdo, mas com uma melhor manipulação do comportamento HTTP e WSGI.

Isso lê o corpo inteiro na memória, o que será um problema se, por exemplo, um arquivo grande for postado. Isso não lerá nada se o Content-Lengthcabeçalho estiver ausente, portanto, não manipulará solicitações de streaming.

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
fonte
6

request.dataficará vazio se request.headers["Content-Type"]for reconhecido como dados do formulário, que serão analisados request.form. Para obter os dados brutos, independentemente do tipo de conteúdo, use request.get_data().

request.datachamadas request.get_data(parse_form_data=True), o que resulta no comportamento diferente dos dados do formulário.

KevinH
fonte