Holen Sie sich den rohen POST-Body in Python Flask unabhängig vom Content-Type-Header

131

Zuvor habe ich gefragt, wie Daten in der Flask-Anfrage empfangen werden sollen, da sie request.dataleer sind. In der Antwort wurde erklärt, dass dies request.datader rohe Post-Body ist, aber leer ist, wenn Formulardaten analysiert werden. Wie kann ich den rohen Postkörper bedingungslos bekommen?

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

Antworten:

218

Verwenden Sie request.get_data()diese Option , um die Rohdaten unabhängig vom Inhaltstyp abzurufen. Die Daten werden zwischengespeichert und Sie können anschließend Zugang request.data, request.json, request.formnach Belieben .

Wenn Sie request.datazuerst zugreifen , wird get_datamit einem Argument aufgerufen , um zuerst Formulardaten zu analysieren. Wenn die Anforderung ein Formular Inhaltstyp hat ( multipart/form-data, application/x-www-form-urlencodedoder application/x-url-encoded) , dann werden die Rohdaten verzehrt werden. request.dataund request.jsonerscheint in diesem Fall leer.

miracle2k
quelle
2
Dies scheint zu brechen, wenn
Raven
34

request.streamist der Strom von Rohdaten, der vom WSGI-Server an die Anwendung übergeben wird. Beim Lesen wird keine Analyse durchgeführt, obwohl Sie dies normalerweise möchten request.get_data().

data = request.stream.read()

Der Stream ist leer, wenn er zuvor von request.dataeinem anderen Attribut gelesen wurde .

jd.
quelle
15

Ich habe eine WSGI-Middleware erstellt, die den Rohkörper aus dem environ['wsgi.input']Stream speichert . Ich habe den Wert in der WSGI-Umgebung gespeichert, damit ich über request.environ['body_copy']meine App darauf zugreifen kann .

Dies ist in Werkzeug oder Flask nicht erforderlich, da request.get_data()die Rohdaten unabhängig vom Inhaltstyp abgerufen werden, jedoch mit einer besseren Handhabung des HTTP- und WSGI-Verhaltens.

Dadurch wird der gesamte Körper in den Speicher eingelesen. Dies ist ein Problem, wenn beispielsweise eine große Datei veröffentlicht wird. Dies liest nichts, wenn der Content-LengthHeader fehlt, sodass Streaming-Anforderungen nicht verarbeitet werden.

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

request.datawird leer sein, wenn request.headers["Content-Type"]es als Formulardaten erkannt wird, in die analysiert wird request.form. Verwenden Sie, um die Rohdaten unabhängig vom Inhaltstyp abzurufenrequest.get_data() .

request.dataAufrufe request.get_data(parse_form_data=True), was zu einem unterschiedlichen Verhalten der Formulardaten führt.

KevinH
quelle