2012-06-12 17 views
81

Anteriormente, le pregunté How to get data received in Flask request porque request.data estaba vacío. La respuesta explicaba que request.data es el cuerpo del mensaje en bruto, pero estará vacío si se analizan los datos del formulario. ¿Cómo puedo obtener el cuerpo de publicación sin procesar incondicionalmente?Obtiene el cuerpo de POST sin procesar en Python Flask independientemente del encabezado Content-Type

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

Este es probablemente un detalle de implementación de werkzeug, el micro-framework subyacente de WSGI, y no Flask. – jathanism

Respuesta

136

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

Si accede primero al request.data, llamará get_data con un argumento para analizar primero los datos del formulario. Si la solicitud tiene un tipo de contenido de formulario (multipart/form-data, application/x-www-form-urlencoded o application/x-url-encoded), se consumirán los datos brutos. request.data y request.json aparecerán vacíos en este caso.

+2

Probablemente esta sea la respuesta aceptada ahora que la biblioteca ha cambiado. –

+4

Comportamiento muy contraintuitivo, por cierto. Como un error. – dmitry

+0

Ftr: la mencionada [fuente vive aquí] (https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/wrappers.py#L448). – dtk

25

Hay request.stream cuando no se reconoce el tipo MIME.

data = request.stream.read() 
+4

¿Pero el request.stream.read() devolverá algo en los casos en que se reconoce el tipo mime? – ddinchev

+0

La documentación no dice y no sé. Supongo que no, porque solo puedes leer la transmisión una vez. –

+4

Leí un comentario de Armin Ronacher de que si usa 'request.json',' request.data', etc., 'request.stream' estará vacío (o parcialmente consumido). No puedo encontrar el comentario, pero basado en esta anécdota, sería cauteloso. –

14

Acabo de tener este problema, y ​​creo que algunos de ustedes podrían beneficiarse de mi solución. Creé una clase de middleware de WSGI que guarda el cuerpo de POST en bruto del socket. Guardé el valor en la variable WSGI 'environ' para poder referirme a él como request.environ ['body_copy'] dentro de mi aplicación Flask.

Debe tener cuidado de que los datos de la publicación no sean demasiado grandes o de que haya problemas de memoria en su servidor.

class WSGICopyBody(object): 
    def __init__(self, application): 
     self.application = application 

    def __call__(self, environ, start_response): 

     from cStringIO import StringIO 
     length = environ.get('CONTENT_LENGTH', '0') 
     length = 0 if length == '' else int(length) 

     body = environ['wsgi.input'].read(length) 
     environ['body_copy'] = body 
     environ['wsgi.input'] = StringIO(body) 

     # Call the wrapped application 
     app_iter = self.application(environ, 
            self._sr_callback(start_response)) 

     # Return modified response 
     return app_iter 

    def _sr_callback(self, start_response): 
     def callback(status, headers, exc_info=None): 

      # Call upstream start_response 
      start_response(status, headers, exc_info) 
     return callback 

app.wsgi_app = WSGICopyBody(app.wsgi_app) 

request.environ['body_copy'] # This is the raw post body you can use in your flask app 
+1

Esta es solo una buena solución si no puede controlar el encabezado Content-Type del cliente. Si puede cambiar eso, entonces la respuesta a continuación es infinitamente más fácil y simple. –

+0

Estoy trabajando con un cliente que se porta mal. (No tengo control sobre el cliente.) Cliente que envía Content-Type: application/x-www-form-urlencoded pero la carga es raw XML. Flask está decodificando como una forma pero dando la clave como '

+0

Desafortunadamente, Bottle consume el flujo de solicitud y no hay una API concreta que corrija esto a menos que toque el código fuente. Es por eso que personalmente cambié a Falcon. – Farsheed

4

finalmente me di cuenta de si hacer esto:

request.environ['CONTENT_TYPE'] = 'application/something_Flask_ignores' 

Entonces request.data realidad tendrá los datos de envío. Esto es así si no puede controlar la solicitud del cliente y desea anularla en el servidor.

+0

no funciona para mí. 'request.data' todavía está vacío. Solo tiene datos si el cliente envía 'Content-type: application/json'. – fiatjaf

+0

tal hacky. no, gracias. – deweydb

Cuestiones relacionadas