Uzyskaj surową treść POST w Python Flask niezależnie od nagłówka Content-Type


135

Wcześniej zapytałem, jak uzyskać dane otrzymane w żądaniu Flask, ponieważ request.databyło puste. Odpowiedź wyjaśniła, że request.datajest to surowa treść wiadomości, ale będzie pusta, jeśli dane formularza zostaną przeanalizowane. Jak mogę bezwarunkowo otrzymać surową treść postu?

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

Odpowiedzi:


224

Służy request.get_data()do pobierania surowych danych, niezależnie od typu zawartości. Dane są buforowane i można następnie dostępu request.data, request.json, request.formdo woli.

Jeśli uzyskasz dostęp request.datapierwszy, najpierw wywoła get_dataargument, aby przeanalizować dane formularza. Jeśli żądanie ma postać typu zawartości ( multipart/form-data, application/x-www-form-urlencodedlub application/x-url-encoded), a następnie dane surowe zostanie skonsumowane. request.datai request.jsonbędzie w tym przypadku puste.


2
Wydaje się, że to się psuje, gdy używasz raven-python (Sentry), błąd i obejścia tutaj: github.com/getsentry/raven-python/issues/457
— dequis

36

request.streamjest strumieniem nieprzetworzonych danych przekazanych do aplikacji przez serwer WSGI. Podczas czytania nie jest wykonywane żadne analizowanie, chociaż zwykle chcesz request.get_data()zamiast tego.

data = request.stream.read()

Strumień będzie pusty, jeśli został wcześniej odczytany przez request.datalub inny atrybut.


Dziękujemy za wskazanie, że strumień będzie pusty, jeśli wcześniej przeczytasz request.data! Prawie
— dopadł

15

Stworzyłem oprogramowanie pośredniczące WSGI, które przechowuje surowe treści ze environ['wsgi.input']strumienia. Zapisałem wartość w środowisku WSGI, aby mieć do niej dostęp z request.environ['body_copy']poziomu mojej aplikacji.

Nie jest to konieczne w Werkzeug lub Flask, ponieważ request.get_data()spowoduje to pobranie surowych danych niezależnie od typu zawartości, ale z lepszą obsługą zachowania HTTP i WSGI.

Powoduje to wczytanie całego ciała do pamięci, co będzie problemem, jeśli na przykład opublikowany zostanie duży plik. Nie odczyta niczego, jeśli Content-Lengthbrakuje nagłówka, więc nie obsługuje żądań przesyłania strumieniowego.

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']

7

request.databędzie pusty, jeśli request.headers["Content-Type"]zostanie rozpoznany jako dane formularza, do którego zostaną przetworzone request.form. Aby uzyskać nieprzetworzone dane niezależnie od typu zawartości, użyj request.get_data().

request.datawywołań request.get_data(parse_form_data=True), co powoduje różne zachowanie danych formularza.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.