Comment obtenir JSON POSTÉ dans Flask?

326

J'essaie de construire une API simple à l'aide de Flask, dans laquelle je veux maintenant lire du JSON POSTÉ. Je fais le POST avec l'extension Postman Chrome, et le JSON I POST l'est tout simplement {"text":"lalala"}. J'essaie de lire le JSON en utilisant la méthode suivante:

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

Dans le navigateur, il renvoie correctement l'UUID que j'ai mis dans le GET, mais sur la console, il s'imprime simplement None(là où je m'attends à ce qu'il imprime le {"text":"lalala"}. Est-ce que quelqu'un sait comment obtenir le JSON publié à partir de la méthode Flask?

kramer65
la source

Réponses:

428

Tout d'abord, l' .jsonattribut est une propriété qui délègue à la request.get_json()méthode , ce qui explique pourquoi vous voyez Noneici.

Vous devez définir le type de contenu de la demande application/jsonpour que la .jsonpropriété et la .get_json()méthode (sans argument) fonctionnent, car l'une ou l' Noneautre produira autrement. Voir la documentation de FlaskRequest :

Il contiendra les données JSON analysées si le mimetype indique JSON ( application / json , voir is_json()), sinon il le sera None.

Vous pouvez dire request.get_json()de sauter l'exigence de type de contenu en lui passant l' force=Trueargument mot - clé.

Notez que si une exception est déclenchée à ce stade (entraînant éventuellement une réponse 400 Bad Request), vos données JSON ne sont pas valides. Il est en quelque sorte mal formé; vous pouvez le vérifier avec un validateur JSON.

Martijn Pieters
la source
Je pensais que lorsqu'une exception est levée à ce stade, elle devrait plus probablement entraîner une réponse de 500 erreurs internes, n'est-ce pas?
iBug
101

Pour référence, voici le code complet pour savoir comment envoyer json à partir d'un client Python:

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

L'entrée "json =" définira automatiquement le type de contenu, comme indiqué ici: Poster JSON à l'aide de requêtes Python

Et le client ci-dessus fonctionnera avec ce code côté serveur:

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)
Luc
la source
71

C'est comme ça que je le ferais et ça devrait être

@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

Avec silent=Trueset, la get_jsonfonction échouera silencieusement lors de la tentative de récupération du corps json. Par défaut, il est défini sur False. Si vous attendez toujours un corps json (pas en option), laissez-le comme silent=False.

Le paramètre force=Trueignorera la request.headers.get('Content-Type') == 'application/json'vérification que le flacon fait pour vous. Par défaut, il est également défini sur False.

Voir la documentation du flacon .

Je recommanderais fortement de quitter force=Falseet de faire en sorte que le client envoie l'en- Content-Typetête pour le rendre plus explicite.

J'espère que cela t'aides!

radtek
la source
2
Cela dépend si le corps json est facultatif ou non, cela dépend donc de votre cas
radtek
2
Je ne vois aucun cas où cela aurait du sens de publier parfois du json valide et d'autres fois du json invalide. Cela ressemble à deux points de terminaison différents
vidstige
1
Comme je l'ai dit, si un point de terminaison prend un corps json "facultatif", vous pouvez l'utiliser silent=True. Oui, c'est possible et je l'utilise. C'est vraiment basé sur la façon dont vous concevez votre API pour qu'elle soit consommée. S'il n'y a pas de cas comme celui-ci pour votre point de terminaison, supprimez-le silent=Trueou définissez-le explicitement False.
radtek
Pour plus de clarté, l' affiche print(content)après content = request.get_json()l'objet ... mais comme un objet Python valide (et non comme un objet JSON valide). Par exemple, il utilise des guillemets simples tandis que JSON requiert strictement des guillemets doubles pour les valeurs de clé (chaînes) en tant que valeurs de chaîne. Si vous voulez la représentation JSON, utilisez-la json.dumps()avec l'objet.
Jochem Schulenklopper
24

En supposant que vous avez publié un JSON valide avec le application/jsontype de contenu, request.jsonles données JSON seront analysées.

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/echo', methods=['POST'])
def hello():
   return jsonify(request.json)
trojek
la source
3
Pour ajouter à cette réponse, la demande que vous pourriez envoyer à ce point de terminaison pourrait être response = request.post('http://127.0.0.1:5000/hello', json={"foo": "bar"}). Suite à cette course response.json()devrait revenir{'foo': 'bar'}
ScottMcC
Il peut être noté que ce {'foo': 'bar'}n'est pas un JSON valide. Il peut s'agir d'une représentation d'objet Python valide qui ressemble beaucoup à JSON, mais JSON valide utilise strictement des guillemets doubles.
Jochem Schulenklopper
@JochemSchulenklopper, la get_json()méthode de requête décode de JSON en objets Python, oui. Où vous attendez-vous à ce qu'il produise un document JSON valide?
Martijn Pieters
@MartijnPieters, je faisais juste une déclaration sur une particularité qui m'a mordu au moins deux fois :-) Mais oui, normalement j'attends une fonction appelée .json()ou .get_json()pour renvoyer une représentation d'objet JSON valide, pas un dict Python. Je regarde juste le nom et j'en déduis ce qui pourrait en sortir.
Jochem Schulenklopper
6

Pour tous ceux dont le problème provenait de l'appel ajax, voici un exemple complet:

Appel Ajax: la clé ici est d'utiliser un dictpuisJSON.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",
    });

Et côté serveur:

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
la source
1
C'est la chose qui a fonctionné pour moi, merci beaucoup! :)
vjjj
1

Pour donner une autre approche.

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
la source
0

En supposant que vous avez publié un JSON valide,

@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
la source
0

Essayez d'utiliser le paramètre force ...

request.get_json(force = True)

Tremper
la source