Obtenez le corps POST brut dans Python Flask quel que soit l'en-tête Content-Type

131

Auparavant, j'ai demandé comment obtenir les données reçues dans la demande Flask car elle request.dataétait vide. La réponse a expliqué qu'il request.datas'agit du corps brut de l'article, mais qu'il sera vide si les données du formulaire sont analysées. Comment puis-je obtenir le corps du courrier brut sans condition?

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

Réponses:

218

Utilisez request.get_data()pour obtenir les données brutes, quel que soit le type de contenu. Les données sont mises en cache et vous pouvez accéder ultérieurement request.data, request.json, request.formà volonté.

Si vous accédez en request.datapremier, il appellera get_dataavec un argument pour analyser les données du formulaire en premier. Si la demande a un type de contenu de forme ( multipart/form-data, application/x-www-form-urlencodedou application/x-url-encoded) , puis les données brutes seront consommées. request.dataet request.jsonapparaîtra vide dans ce cas.

miracle2k
la source
2
Cela semble casser lors de l'utilisation de raven-python (Sentry), bogue et solutions de contournement ici: github.com/getsentry/raven-python/issues/457
dequis
34

request.streamest le flux de données brutes transmis à l'application par le serveur WSGI. Aucune analyse n'est effectuée lors de sa lecture, bien que vous souhaitiez généralement la request.get_data()remplacer.

data = request.stream.read()

Le flux sera vide s'il a été précédemment lu par request.dataou par un autre attribut.

jd.
la source
15

J'ai créé un middleware WSGI qui stocke le corps brut du environ['wsgi.input']flux. J'ai enregistré la valeur dans l'environnement WSGI afin de pouvoir y accéder depuis request.environ['body_copy']mon application.

Cela n'est pas nécessaire dans Werkzeug ou Flask, car cela request.get_data()permettra d'obtenir les données brutes quel que soit le type de contenu, mais avec une meilleure gestion du comportement HTTP et WSGI.

Cela lit le corps entier en mémoire, ce qui sera un problème si, par exemple, un fichier volumineux est publié. Cela ne lit rien si l'en- Content-Lengthtête est manquant, donc il ne traitera pas les demandes 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
la source
6

request.datasera vide si request.headers["Content-Type"]est reconnu comme des données de formulaire, qui seront analysées request.form. Pour obtenir les données brutes quel que soit le type de contenu, utilisez request.get_data().

request.dataappels request.get_data(parse_form_data=True), ce qui entraîne un comportement différent pour les données de formulaire.

KevinH
la source