Как получить POSTed JSON во Flask?

326

Я пытаюсь создать простой API с помощью Flask, в котором я сейчас хочу прочитать некоторые POSTed JSON. Я делаю POST с расширением Postman Chrome, а JSON I POST просто {"text":"lalala"}. Я пытаюсь прочитать JSON, используя следующий метод:

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

В браузере он правильно возвращает UUID, который я вставил в GET, но на консоли он просто распечатывает None(где я ожидаю, что он распечатает {"text":"lalala"}. Кто-нибудь знает, как я могу получить опубликованный JSON из метода Flask?

kramer65
источник

Ответы:

428

Прежде всего, .jsonатрибут является свойством, которое делегирует request.get_json()методу , который документирует, почему вы видите Noneздесь.

Вам нужно установить тип содержимого запроса application/jsonдля .jsonсвойства и .get_json()метода (без аргументов) для работы, так как любой из них приведет к Noneдругому. Смотрите документацию FlaskRequest :

Он будет содержать проанализированные данные JSON, если mimetype указывает JSON ( application / json , см. is_json()), Иначе это будет так None.

Вы можете request.get_json()пропустить требование к типу контента, передав ему force=Trueаргумент ключевого слова.

Обратите внимание, что если в этот момент возникает исключение (возможно, приводящее к ответу 400 Bad Request), ваши данные JSON являются недействительными. Это в некотором роде искажено; Вы можете проверить это с помощью валидатора JSON.

Мартейн Питерс
источник
Я думал, что когда возникает исключение в этой точке, это, скорее всего, приведет к ответу 500 Internal Error, не так ли?
iBug
101

Для справки вот полный код для отправки json из клиента Python:

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

Вход "json =" автоматически установит тип содержимого, как описано здесь: публикация JSON с использованием запросов Python

И вышеупомянутый клиент будет работать с этим кодом на стороне сервера:

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)
Люк
источник
71

Это то, как я бы это сделал, и это должно быть

@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

При silent=Trueустановке get_jsonфункция молча завершится ошибкой при попытке получить тело json. По умолчанию это установлено на False. Если вы всегда ожидаете тело json (не обязательно), оставьте его как silent=False.

Настройка force=Trueигнорирует request.headers.get('Content-Type') == 'application/json'проверку, которую делает колба для вас. По умолчанию это также установлено в False.

Смотрите документацию на колбу .

Я настоятельно рекомендую оставить force=Falseи заставить клиента отправлять Content-Typeзаголовок, чтобы сделать его более явным.

Надеюсь это поможет!

radtek
источник
2
Зависит от того, является ли тело json необязательным или нет, так что зависит от вашего случая
radtek
2
Я не вижу ни одного случая, когда было бы целесообразно несколько раз публиковать действительный json, а иногда недействительный json. Звуки , как два различных конечных точек
vidstige
1
Как я уже сказал, если конечная точка принимает «необязательное» тело json, вы можете использовать silent=True. Да, это возможно, и я использую это. Он действительно основан на том, как вы разрабатываете свой API для использования. Если для вашей конечной точки не существует такого случая, просто удалите silent=Trueили явно установите для него значение False.
Радтек
Для ясности, print(content)after content = request.get_json()печатает объект ... но как действительный объект Python (а не как действительный объект JSON). Например, он использует одинарные кавычки, в то время как JSON строго требует двойных кавычек для обоих ключевых значений (строк) в качестве строковых значений. Если вы хотите представление JSON, используйте json.dumps()с объектом.
Йохем Шуленклоппер
24

Предполагая, что вы опубликовали действительный JSON с application/jsonтипом контента, request.jsonбудет иметь проанализированные данные JSON.

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/echo', methods=['POST'])
def hello():
   return jsonify(request.json)
trojek
источник
3
Чтобы добавить к этому ответу запрос, который вы могли бы отправить к этой конечной точке, может быть response = request.post('http://127.0.0.1:5000/hello', json={"foo": "bar"}). После этого бега response.json()должен вернуться{'foo': 'bar'}
ScottMcC
Можно отметить, что {'foo': 'bar'}это не действительно JSON. Это может быть допустимое представление объекта Python, которое во многом похоже на JSON, но действительный JSON строго использует двойные кавычки.
Йохем Шуленклоппер
@JochemSchulenklopper get_json()метод запроса декодирует из JSON в объекты Python, да. Где вы ожидаете, что он создаст действительный документ JSON?
Мартин Питерс
@MartijnPieters, я просто делал заявление об особенности, которая поразила меня, по крайней мере, дважды :-) Но да, обычно я ожидаю, что функция, вызываемая .json()или .get_json()возвращающая действительное представление объекта JSON, а не Python. Я просто смотрю на имя и делаю вывод, что из этого может получиться.
Йохем Шуленклоппер
6

Для всех тех, чья проблема была связана с вызовом ajax, вот полный пример:

Ajax-вызов: ключом здесь является использование, dictа затемJSON.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",
    });

И на стороне сервера:

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
источник
1
Это то, что сработало для меня, большое спасибо! :)
vjjj
1

Чтобы дать другой подход.

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)
Омер Табан
источник
0

Предполагая, что вы опубликовали действительный JSON,

@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)
Раджахмад Мулани
источник
0

Попробуйте использовать параметр силы ...

request.get_json(force = True)

наклонение
источник