Como obter JSON no POSTed Flask?

326

Estou tentando criar uma API simples usando o Flask, na qual agora quero ler algum JSON POSTADO. Eu faço o POST com a extensão Postman Chrome, e o JSON I POST é simples {"text":"lalala"}. Eu tento ler o JSON usando o seguinte método:

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

No navegador, ele retorna corretamente o UUID que eu coloquei no GET, mas no console, ele apenas imprime None(onde eu espero que ele imprima o {"text":"lalala"}. Alguém sabe como eu posso obter o JSON postado no método Flask?

kramer65
fonte

Respostas:

428

Primeiro de tudo, o .jsonatributo é uma propriedade que delega ao request.get_json()método , que documenta por que você vê Noneaqui.

Você precisa definir o tipo de conteúdo da solicitação application/jsonpara que a .jsonpropriedade e o .get_json()método (sem argumentos) funcionem, pois ambos produzirão de Noneoutra forma. Veja a documentação do FlaskRequest :

Isso conterá os dados JSON analisados ​​se o tipo de mimet indicar JSON ( application / json , consulte is_json()), caso contrário, será None.

Você pode dizer request.get_json()para ignorar o requisito de tipo de conteúdo passando o force=Trueargumento de palavra - chave.

Observe que, se uma exceção for gerada neste momento (possivelmente resultando em uma resposta 400 Bad Request), seus dados JSON serão inválidos. Está de alguma forma malformado; convém verificar com um validador JSON.

Martijn Pieters
fonte
Eu pensei que quando uma exceção é gerada neste momento, deve resultar mais provavelmente em uma resposta de 500 erros internos, não é?
iBug
101

Para referência, aqui está o código completo de como enviar json a partir de um cliente Python:

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

A entrada "json =" definirá automaticamente o tipo de conteúdo, conforme discutido aqui: Poste JSON usando solicitações de Python

E o cliente acima funcionará com este código do lado do servidor:

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)
Lucas
fonte
71

Esta é a maneira que eu faria e deveria ser

@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

Com silent=Trueset, a get_jsonfunção falhará silenciosamente ao tentar recuperar o corpo do json. Por padrão, isso está definido como False. Se você está sempre esperando um corpo de json (não opcionalmente), deixe-o como silent=False.

A configuração force=Trueignorará a request.headers.get('Content-Type') == 'application/json'verificação que o balão faz por você. Por padrão, isso também é definido como False.

Consulte a documentação do balão .

Eu recomendo sair force=Falsee fazer o cliente enviar o Content-Typecabeçalho para torná-lo mais explícito.

Espero que isto ajude!

Radtek
fonte
2
Depende se o corpo json é opcional ou não, então depende do seu caso
radtek
2
Não consigo ver nenhum caso em que faria sentido algumas vezes postar json válido e outras vezes json inválido. Soa como dois pontos finais diferentes
vidstige 05/10
1
Como eu disse, se um nó de extremidade receber o corpo json "opcional", você poderá usar silent=True. Sim, isso é possível, e eu uso. É realmente baseado em como você projeta sua API para ser consumida. Se não houver um caso como esse para o terminal, remova silent=Trueou defina explicitamente como False.
Radtek 5/10
Para maior clareza, o print(content)after content = request.get_json()imprime o objeto ... mas como um objeto Python válido (e não como um objeto JSON válido). Por exemplo, ele usa aspas simples, enquanto o JSON exige estritamente aspas duplas para os valores de chave (cadeias) como valores de cadeia. Se você deseja a representação JSON, use json.dumps()com o objeto
Jochem Schulenklopper 12/11/19
24

Supondo que você tenha publicado JSON válido com o application/jsontipo de conteúdo, request.jsonos dados JSON serão analisados.

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/echo', methods=['POST'])
def hello():
   return jsonify(request.json)
trojek
fonte
3
Para adicionar a essa resposta, a solicitação que você poderia enviar para este terminal poderia ser response = request.post('http://127.0.0.1:5000/hello', json={"foo": "bar"}). Seguindo essa execução response.json()deve retornar{'foo': 'bar'}
ScottMcC
Pode-se notar que o {'foo': 'bar'}JSON não é válido. Pode ser uma representação de objeto Python válida que se parece muito com JSON, mas JSON válido usa estritamente aspas duplas.
Jochem Schulenklopper 12/11/19
@JochemSchulenklopper, o get_json()método request decodifica de objetos JSON para Python, sim. Onde você espera que ele produza um documento JSON válido?
Martijn Pieters
@ MartijnPieters, eu estava apenas fazendo uma declaração sobre uma peculiaridade que me mordeu pelo menos duas vezes :-) Mas sim, normalmente espero que uma função seja chamada .json()ou .get_json()retorne uma representação de objeto JSON válida, não um ditado de Python. Estou apenas olhando o nome e inferir o que pode resultar disso.
Jochem Schulenklopper 22/05/19
6

Para todos aqueles cujo problema ocorreu na chamada ajax, aqui está um exemplo completo:

Chamada Ajax: a chave aqui é usar um dicte depoisJSON.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",
    });

E no lado do servidor:

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
fonte
1
Isso é o que funcionou para mim, muito obrigado! :)
vjjj 22/04
1

Para dar outra abordagem.

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
fonte
0

Supondo que você tenha postado JSON válido,

@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
fonte
0

Tente usar o parâmetro force ...

request.get_json(force = True)

Mergulho
fonte