Wie bekomme ich POSTed JSON in Flask?

324

Ich versuche, mit Flask eine einfache API zu erstellen, in der ich jetzt POSTed JSON lesen möchte. Ich mache den POST mit der Postman Chrome-Erweiterung und der JSON I POST ist einfach {"text":"lalala"}. Ich versuche, den JSON mit der folgenden Methode zu lesen:

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.json
    print content
    return uuid

Im Browser wird die UUID, die ich in das GET eingegeben habe, korrekt zurückgegeben, aber auf der Konsole wird sie nur ausgedruckt None(wo ich erwarte, dass sie die druckt {"text":"lalala"}. Weiß jemand, wie ich den geposteten JSON über die Flask-Methode abrufen kann?

kramer65
quelle

Antworten:

427

Zuallererst ist das .jsonAttribut eine Eigenschaft, die an die request.get_json()Methode delegiert , die dokumentiert, warum Sie Nonehier sehen.

Sie müssen den Anforderungsinhaltstyp auf festlegen, application/jsondamit die .jsonEigenschaft und die .get_json()Methode (ohne Argumente) funktionieren, wie dies Nonesonst der Fall ist. Siehe die Flask RequestDokumentation :

Dies enthält die analysierten JSON-Daten, wenn der Mimetyp JSON angibt ( application / json , siehe is_json()), andernfalls None.

Sie können request.get_json()festlegen, dass die Anforderung für den Inhaltstyp übersprungen werden soll, indem Sie das force=TrueSchlüsselwortargument übergeben.

Beachten Sie, dass Ihre JSON- Daten ungültig sind , wenn zu diesem Zeitpunkt eine Ausnahme ausgelöst wird (was möglicherweise zu einer Antwort auf 400 Bad Request führt) . Es ist in irgendeiner Weise missgebildet; Möglicherweise möchten Sie dies mit einem JSON-Validator überprüfen.

Martijn Pieters
quelle
Ich dachte, wenn an dieser Stelle eine Ausnahme ausgelöst wird, sollte dies eher zu einer Reaktion auf 500 interne Fehler führen, nicht wahr?
iBug
99

Als Referenz finden Sie hier den vollständigen Code zum Senden von JSON von einem Python-Client:

import requests
res = requests.post('http://localhost:5000/api/add_message/1234', json={"mytext":"lalala"})
if res.ok:
    print res.json()

Die Eingabe "json =" legt automatisch den Inhaltstyp fest, wie hier beschrieben: JSON mit Python-Anforderungen posten

Und der obige Client funktioniert mit diesem serverseitigen Code:

from flask import Flask, request, jsonify
app = Flask(__name__)

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.json
    print content['mytext']
    return jsonify({"uuid":uuid})

if __name__ == '__main__':
    app.run(host= '0.0.0.0',debug=True)
Luke
quelle
70

So würde ich es machen und es sollte so sein

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.get_json(silent=True)
    # print(content) # Do your processing
    return uuid

Mit silent=Trueset get_jsonschlägt die Funktion stillschweigend fehl, wenn versucht wird, den JSON-Body abzurufen. Standardmäßig ist dies auf eingestellt False. Wenn Sie immer einen JSON-Körper erwarten (nicht optional), lassen Sie ihn als silent=False.

Die Einstellung force=Trueignoriert die request.headers.get('Content-Type') == 'application/json'Prüfung, die der Kolben für Sie durchführt. Standardmäßig ist dies ebenfalls eingestellt False.

Siehe Kolbendokumentation .

Ich würde dringend empfehlen force=False, den Content-TypeHeader zu verlassen und den Header zu senden , um ihn expliziter zu machen.

Hoffe das hilft!

radtek
quelle
2
Hängt davon ab, ob der JSON-Körper optional ist oder nicht, hängt also von Ihrem Fall ab
Radtek
2
Ich kann keinen Fall sehen, in dem es sinnvoll wäre, manchmal gültigen json und manchmal ungültigen json zu posten. Klingt nach zwei verschiedenen Endpunkten
vidstige
1
Wie gesagt, wenn ein Endpunkt "optional" json body verwendet, können Sie verwenden silent=True. Ja, das ist möglich und ich benutze es. Es basiert wirklich darauf, wie Sie Ihre API für den Verbrauch entwerfen. Wenn es für Ihren Endpunkt keinen solchen Fall gibt, entfernen Sie ihn einfach silent=Trueoder setzen Sie ihn explizit auf False.
Radtek
Aus Gründen der Übersichtlichkeit druckt das print(content)After content = request.get_json()das Objekt ... aber als gültiges Python-Objekt (und nicht als gültiges JSON-Objekt). Beispielsweise werden einfache Anführungszeichen verwendet, während JSON ausschließlich doppelte Anführungszeichen für beide Schlüsselwerte (Zeichenfolgen) als Zeichenfolgenwerte erfordert. Wenn Sie die JSON-Darstellung möchten, verwenden Sie sie json.dumps()mit dem Objekt.
Jochem Schulenklopper
24

Angenommen, Sie haben gültiges JSON mit dem application/jsonInhaltstyp veröffentlicht, request.jsonwerden die analysierten JSON-Daten angezeigt.

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/echo', methods=['POST'])
def hello():
   return jsonify(request.json)
trojek
quelle
3
Um dieser Antwort die Anfrage hinzuzufügen, die Sie an diesen Endpunkt senden könnten response = request.post('http://127.0.0.1:5000/hello', json={"foo": "bar"}). Nach diesem Lauf response.json()sollte zurückkehren{'foo': 'bar'}
ScottMcC
Es kann jedoch festgestellt werden, dass {'foo': 'bar'}JSON nicht gültig ist. Es könnte sich um eine gültige Python-Objektdarstellung handeln, die JSON sehr ähnlich sieht, aber für gültiges JSON werden ausschließlich doppelte Anführungszeichen verwendet.
Jochem Schulenklopper
@JochemSchulenklopper Die Anforderungsmethode get_json()dekodiert von JSON in Python-Objekte, ja. Wo erwarten Sie ein gültiges JSON-Dokument?
Martijn Pieters
@MartijnPieters, ich habe gerade eine Aussage über eine Besonderheit gemacht, die mich mindestens zweimal gebissen hat :-) Aber ja, normalerweise erwarte ich, dass eine Funktion aufgerufen wird .json()oder .get_json()eine gültige JSON-Objektdarstellung zurückgibt, kein Python-Diktat. Ich schaue nur auf den Namen und schließe daraus, was daraus entstehen könnte.
Jochem Schulenklopper
4

Für alle, deren Problem vom Ajax-Aufruf herrührte, ist hier ein vollständiges Beispiel:

Ajax-Aufruf: Der Schlüssel hier ist, ein dictund dann zu verwendenJSON.stringify

    var dict = {username : "username" , password:"password"};

    $.ajax({
        type: "POST", 
        url: "http://127.0.0.1:5000/", //localhost Flask
        data : JSON.stringify(dict),
        contentType: "application/json",
    });

Und auf der Serverseite:

from flask import Flask
from flask import request
import json

app = Flask(__name__)

@app.route("/",  methods = ['POST'])
def hello():
    print(request.get_json())
    return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 

if __name__ == "__main__":
    app.run()
Arcyno
quelle
1
Das hat bei mir funktioniert, vielen Dank! :)
vjjj
1

Um einen anderen Ansatz zu geben.

from flask import Flask, jsonify, request
app = Flask(__name__)

@app.route('/service', methods=['POST'])
def service():
    data = json.loads(request.data)
    text = data.get("text",None)
    if text is None:
        return jsonify({"message":"text not found"})
    else:
        return jsonify(data)

if __name__ == '__main__':
    app.run(host= '0.0.0.0',debug=True)
Ömer Taban
quelle
0

Angenommen, Sie haben einen gültigen JSON veröffentlicht.

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.json
    print content['uuid']
    # Return data as JSON
    return jsonify(content)
RAJAHMAD MULANI
quelle
0

Versuchen Sie, den Force-Parameter zu verwenden ...

request.get_json(force = True)

Tauchen
quelle