Jak zdobyć POSTed JSON w Flask?

326

Usiłuję zbudować prosty interfejs API za pomocą Flask, w którym chcę teraz przeczytać trochę POSTed JSON. Wykonuję POST z rozszerzeniem Postman Chrome, a JSON I POST jest po prostu {"text":"lalala"}. Próbuję odczytać JSON przy użyciu następującej metody:

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

W przeglądarce poprawnie zwraca UUID, który umieściłem w GET, ale na konsoli po prostu drukuje None(tam, gdzie spodziewam się, że wydrukuje {"text":"lalala"}. Czy ktoś wie, jak mogę uzyskać wysłany JSON z metody Flask?

kramer65
źródło

Odpowiedzi:

427

Przede wszystkim .jsonatrybut jest właściwością delegowaną do request.get_json()metody , która dokumentuje, dlaczego widzisz Nonetutaj.

Musisz ustawić typ zawartości żądania na, application/jsonaby .jsonwłaściwość i .get_json()metoda (bez argumentów) działały, ponieważ w Noneprzeciwnym razie powstanie jedna z nich . Zobacz dokumentację FlaskRequest :

Będzie zawierał przeanalizowane dane JSON, jeśli typ mimetyczny wskazuje JSON ( application / json , patrz is_json()), w przeciwnym razie będzie None.

Możesz powiedzieć, request.get_json()aby pominąć wymaganie dotyczące typu treści, przekazując mu force=Trueargument słowa kluczowego.

Zauważ, że jeśli w tym momencie zostanie zgłoszony wyjątek (być może skutkujący odpowiedzią na błędne żądanie 400), dane JSON są nieprawidłowe. Jest w jakiś sposób zniekształcony; możesz to sprawdzić za pomocą walidatora JSON.

Martijn Pieters
źródło
Pomyślałem, że kiedy w tym momencie zostanie zgłoszony wyjątek, bardziej prawdopodobne jest, że wystąpi odpowiedź 500 Błąd wewnętrzny, prawda?
iBug
100

Dla porównania, oto pełny kod wysyłania jsona z klienta Python:

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

Wejście „json =” automatycznie ustawi typ zawartości, jak omówiono tutaj: Opublikuj JSON za pomocą Pythona

Powyższy klient będzie działał z tym kodem po stronie serwera:

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)
Łukasz
źródło
71

Tak właśnie bym to zrobił i tak powinno być

@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

Po silent=Trueustawieniu get_jsonfunkcja nie powiedzie się podczas próby pobrania treści JSON. Domyślnie jest to ustawione na False. Jeśli zawsze oczekujesz ciała JSON (nie opcjonalnie), pozostaw to jako silent=False.

Ustawienie force=Truezignoruje request.headers.get('Content-Type') == 'application/json'sprawdzenie, które wykonuje dla ciebie kolba. Domyślnie jest to również ustawione na False.

Zobacz dokumentację kolby .

Zdecydowanie polecam odejście force=Falsei sprawienie, by klient wysłał Content-Typenagłówek, aby był bardziej wyraźny.

Mam nadzieję że to pomoże!

radtek
źródło
2
Zależy, czy ciało Jsona jest opcjonalne, czy nie, więc zależy od twojej sprawy
radtek
2
Nie widzę żadnego przypadku, w którym ma to sens, aby w niektórych przypadkach opublikować prawidłowy Json, a innym razem nieprawidłowy Json. Brzmi jak dwa różne punkty końcowe
vidstige
1
Tak jak powiedziałem, jeśli punkt końcowy przyjmuje „opcjonalne” ciało Json, możesz użyć silent=True. Tak, jest to możliwe i ja z niego korzystam. Jest naprawdę oparty na sposobie projektowania interfejsu API do wykorzystania. Jeśli nie ma takiego przypadku dla twojego punktu końcowego, po prostu usuń go silent=Truelub jawnie ustaw na False.
radtek
Dla jasności, print(content)po content = request.get_json()drukuje obiekt ... ale jako prawidłowy obiekt Python (a nie jako prawidłowy obiekt JSON). Na przykład używa pojedynczych cudzysłowów, podczas gdy JSON ściśle wymaga podwójnych cudzysłowów dla obu kluczowych wartości (ciągów) jako wartości ciągów. Jeśli chcesz reprezentację JSON, użyj json.dumps()z tym obiektem.
Jochem Schulenklopper
24

Zakładając, że opublikowałeś prawidłowy JSON z application/jsontypem treści, request.jsonbędzie miał przeanalizowane dane JSON.

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/echo', methods=['POST'])
def hello():
   return jsonify(request.json)
trojek
źródło
3
Aby dodać do tej odpowiedzi, prośba, którą możesz wysłać do tego punktu końcowego, może być response = request.post('http://127.0.0.1:5000/hello', json={"foo": "bar"}). Po tym uruchomieniu response.json()powinien wrócić{'foo': 'bar'}
ScottMcC
Można zauważyć, że {'foo': 'bar'}nie jest to prawidłowy JSON. Może to być poprawna reprezentacja obiektu w języku Python, która wygląda bardzo podobnie do JSON, ale poprawny JSON ściśle wykorzystuje podwójne cudzysłowy.
Jochem Schulenklopper
@JochemSchulenklopper get_json()metoda żądania dekoduje z JSON do obiektów Python, tak. Gdzie spodziewasz się, że wygeneruje ważny dokument JSON?
Martijn Pieters
@MartijnPieters, właśnie wypowiadałem się o osobliwości, która przynajmniej dwa razy mnie ugryzła :-) Ale tak, zwykle oczekuję, że funkcja wywoła .json()lub .get_json()zwróci prawidłową reprezentację obiektu JSON, a nie dyktę Pythona. Patrzę tylko na nazwę i wywnioskuję, co może z niej wyniknąć.
Jochem Schulenklopper
5

Dla wszystkich, których problem dotyczył wywołania ajax, oto pełny przykład:

Wywołanie Ajax: kluczem jest użycie a, dicta następnieJSON.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",
    });

A po stronie serwera:

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
źródło
1
To właśnie dla mnie działało, wielkie dzięki! :)
vjjj
1

Podać inne podejście.

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
źródło
0

Zakładając, że opublikowałeś prawidłowy 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)
RAJAHMAD MULANI
źródło
0

Spróbuj użyć parametru siły ...

request.get_json(force = True)

Stoczek
źródło