¿Cómo publicar JSON en Flask?

326

Estoy tratando de construir una API simple usando Flask, en el que ahora quiero leer algunos JSON PUBLICADOS. Hago la POST con la extensión Postman Chrome, y la JSON I POST es sencilla {"text":"lalala"}. Intento leer el JSON usando el siguiente método:

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

En el navegador, devuelve correctamente el UUID que puse en el GET, pero en la consola, solo se imprime None(donde espero que imprima {"text":"lalala"}. ¿Alguien sabe cómo puedo obtener el JSON publicado desde el método Flask?

kramer65
fuente

Respuestas:

428

En primer lugar, el .jsonatributo es una propiedad que delega al request.get_json()método , que documenta por qué se ve Noneaquí.

Debe establecer el tipo de contenido de la solicitud application/jsonpara que la .jsonpropiedad y el .get_json()método (sin argumentos) funcionen, ya que cualquiera de los dos producirá lo Nonecontrario. Consulte la documentación del matrazRequest :

Esto contendrá los datos JSON analizados si el tipo MIME indica JSON ( aplicación / json , ver is_json()), de lo contrario lo será None.

Puede decir request.get_json()que omita el requisito de tipo de contenido pasándole el force=Trueargumento de la palabra clave.

Tenga en cuenta que si se genera una excepción en este punto (posiblemente resultando en una respuesta de 400 Solicitud incorrecta), sus datos JSON no son válidos. De alguna manera está malformada; es posible que desee verificarlo con un validador JSON.

Martijn Pieters
fuente
Pensé que cuando se produce una excepción en este punto, lo más probable es que dé como resultado una respuesta de 500 errores internos, ¿no?
iBug
101

Como referencia, aquí hay un código completo sobre cómo enviar json desde un cliente Python:

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

La entrada "json =" establecerá automáticamente el tipo de contenido, como se explica aquí: Publique JSON usando las solicitudes de Python

Y el cliente anterior funcionará con este código del lado del 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)
Luke
fuente
71

Esta es la forma en que lo haría y debería 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

Con silent=Trueset, la get_jsonfunción fallará en silencio cuando intente recuperar el cuerpo json. Por defecto, esto está configurado en False. Si siempre espera un cuerpo json (no opcionalmente), déjelo como silent=False.

La configuración force=Trueignorará la request.headers.get('Content-Type') == 'application/json'comprobación que el matraz hace por usted. Por defecto, esto también está configurado en False.

Ver la documentación del matraz .

Recomiendo irse force=Falsey hacer que el cliente envíe el Content-Typeencabezado para hacerlo más explícito.

¡Espero que esto ayude!

radtek
fuente
2
Depende de si el cuerpo json es opcional o no, así que depende de su caso
radtek
2
No veo ningún caso en el que tenga sentido publicar algunas veces json válidos y otras veces json inválidos. Suena como dos puntos finales diferentes
vidstige
1
Como dije, si un punto final toma un cuerpo json "opcional", puede usarlo silent=True. Sí, esto es posible, y lo uso. Realmente se basa en cómo diseñas tu API para ser consumida. Si no hay un caso así para su punto final, simplemente elimínelo silent=Trueo configúrelo explícitamente False.
radtek
Para mayor claridad, el print(content)after content = request.get_json()imprime el objeto ... pero como un objeto Python válido (y no como un objeto JSON válido). Por ejemplo, utiliza comillas simples, mientras que JSON requiere estrictamente comillas dobles tanto para los valores clave (cadenas) como para los valores de cadena. Si desea la representación JSON, úsela json.dumps()con el objeto.
Jochem Schulenklopper
24

Suponiendo que haya publicado JSON válido con el application/jsontipo de contenido, request.jsontendrá los datos JSON analizados.

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/echo', methods=['POST'])
def hello():
   return jsonify(request.json)
trojek
fuente
3
Para agregar a esta respuesta, la solicitud que podría enviar a este punto final podría ser response = request.post('http://127.0.0.1:5000/hello', json={"foo": "bar"}). Después de esta ejecución response.json()debería volver{'foo': 'bar'}
ScottMcC
Sin embargo, podría notarse que {'foo': 'bar'}no es válido JSON. Podría ser una representación de objeto Python válida que se parezca mucho a JSON, pero JSON válido usa estrictamente comillas dobles.
Jochem Schulenklopper
@JochemSchulenklopper el get_json()método de solicitud decodifica desde JSON a objetos Python, sí. ¿Dónde espera que produzca un documento JSON válido?
Martijn Pieters
@MartijnPieters, solo estaba haciendo una declaración sobre una peculiaridad que me mordió al menos dos veces :-) Pero sí, normalmente espero que se llame a una función .json()o .get_json()que devuelva una representación de objeto JSON válida, no un dict de Python. Solo estoy mirando el nombre e infiero lo que podría salir de él.
Jochem Schulenklopper
6

Para todos aquellos cuyo problema fue de la llamada ajax, aquí hay un ejemplo completo:

Llamada Ajax: la clave aquí es usar ay dictluegoJSON.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",
    });

Y en el lado del 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
fuente
1
Esto es lo que funcionó para mí, ¡muchas gracias! :)
vjjj
1

Para dar otro enfoque.

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

Suponiendo que ha publicado un 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
fuente
0

Intenta usar el parámetro de fuerza ...

request.get_json(force = True)

Inmersión
fuente