Obtenga un cuerpo POST sin procesar en Python Flask independientemente del encabezado Content-Type

131

Anteriormente, pregunté cómo obtener los datos recibidos en la solicitud de Flask porque request.dataestaba vacío. La respuesta explicó que request.dataes el cuerpo de la publicación sin formato, pero estará vacío si se analizan los datos del formulario. ¿Cómo puedo obtener el cuerpo de la publicación sin procesar 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
fuente

Respuestas:

218

Utilícelo request.get_data()para obtener los datos sin procesar, independientemente del tipo de contenido. Los datos se almacenan en caché y, posteriormente, usted puede acceder request.data, request.json,request.form a voluntad.

Si accede request.dataprimero, llamará get_datacon un argumento para analizar primero los datos del formulario. Si la solicitud tiene un tipo de contenido forma ( multipart/form-data, application/x-www-form-urlencodedo application/x-url-encoded), entonces se consumirá los datos en bruto. request.datay request.jsonaparecerá vacío en este caso.

miracle2k
fuente
2
Esto parece romperse cuando se usa raven-python (Sentry), error y soluciones alternativas aquí: github.com/getsentry/raven-python/issues/457
dequis
34

request.streames la secuencia de datos sin procesar que el servidor WSGI pasa a la aplicación. No se realiza el análisis al leerlo, aunque generalmente desea request.get_data()hacerlo.

data = request.stream.read()

La secuencia estará vacía si fue leída previamente por request.dataotro atributo.

jd.
fuente
15

Creé un middleware WSGI que almacena el cuerpo sin procesar de la environ['wsgi.input']secuencia. Guardé el valor en el entorno WSGI para poder acceder a él desderequest.environ['body_copy'] mi aplicación.

Esto no es necesario en Werkzeug o Flask, ya request.get_data()que obtendrá los datos en bruto independientemente del tipo de contenido, pero con un mejor manejo del comportamiento HTTP y WSGI.

Esto lee todo el cuerpo en la memoria, lo que será un problema si, por ejemplo, se publica un archivo grande. Esto no leerá nada si Content-Lengthfalta el encabezado, por lo que no manejará las solicitudes de transmisión.

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

request.dataestará vacío si request.headers["Content-Type"]se reconoce como datos de formulario, que se analizarán en request.form. Para obtener los datos sin procesar independientemente del tipo de contenido, userequest.get_data() .

request.datallamadas request.get_data(parse_form_data=True), lo que da como resultado un comportamiento diferente para los datos del formulario.

KevinH
fuente