Dapatkan badan POST mentah dalam Flask Python terlepas dari header Tipe-Konten

131

Sebelumnya, saya bertanya Cara mendapatkan data yang diterima dalam permintaan Flask karena request.datakosong. Jawabannya menjelaskan bahwa itu request.dataadalah badan posting mentah, tetapi akan kosong jika data formulir diuraikan. Bagaimana saya bisa mendapatkan postingan mentah tanpa syarat?

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

Jawaban:

218

Gunakan request.get_data()untuk mendapatkan data mentah, apa pun jenis kontennya. Data di-cache dan Anda kemudian dapat mengakses request.data, request.json, request.formdi akan.

Jika Anda mengakses request.datapertama, itu akan memanggil get_datadengan argumen untuk mem-parsing data formulir terlebih dahulu. Jika permintaan memiliki jenis konten bentuk ( multipart/form-data, application/x-www-form-urlencoded, atau application/x-url-encoded) maka data mentah akan dikonsumsi. request.datadan request.jsonakan muncul kosong dalam kasus ini.

miracle2k
sumber
2
Ini tampaknya rusak ketika menggunakan raven-python (Sentry), bug dan workarounds di sini: github.com/getsentry/raven-python/issues/457
dequis
34

request.streamadalah aliran data mentah yang diteruskan ke aplikasi oleh server WSGI. Tidak ada penguraian yang dilakukan saat membacanya, meskipun Anda biasanya menginginkannya request.get_data().

data = request.stream.read()

Aliran akan kosong jika sebelumnya dibaca oleh request.dataatau atribut lain.

jd.
sumber
15

Saya membuat middleware WSGI yang menyimpan bahan mentah dari environ['wsgi.input']sungai. Saya menyimpan nilai di lingkungan WSGI sehingga saya bisa mengaksesnya dari request.environ['body_copy']dalam aplikasi saya.

Ini tidak diperlukan di Werkzeug atau Flask, karena request.get_data()akan mendapatkan data mentah terlepas dari jenis konten, tetapi dengan penanganan yang lebih baik dari perilaku HTTP dan WSGI.

Ini membaca seluruh tubuh ke dalam memori, yang akan menjadi masalah jika misalnya file besar diposting. Ini tidak akan membaca apa pun jika Content-Lengthtajuk tidak ada, sehingga tidak akan menangani permintaan 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']
jaski
sumber
6

request.dataakan kosong jika request.headers["Content-Type"]dikenali sebagai data formulir, yang akan diuraikan menjadi request.form. Untuk mendapatkan data mentah terlepas dari jenis konten, gunakan request.get_data().

request.datapanggilan request.get_data(parse_form_data=True), yang menghasilkan perilaku berbeda untuk data formulir.

KevinH
sumber