Veröffentlichen Sie JSON mithilfe von Python-Anforderungen

630

Ich muss einen JSON von einem Client auf einen Server POSTEN. Ich benutze Python 2.7.1 und simplejson. Der Client verwendet Requests. Der Server ist CherryPy. Ich kann einen fest codierten JSON vom Server abrufen (Code nicht gezeigt), aber wenn ich versuche, einen JSON auf den Server zu POSTEN, erhalte ich "400 Bad Request".

Hier ist mein Kundencode:

data = {'sender':   'Alice',
    'receiver': 'Bob',
    'message':  'We did it!'}
data_json = simplejson.dumps(data)
payload = {'json_payload': data_json}
r = requests.post("http://localhost:8080", data=payload)

Hier ist der Servercode.

class Root(object):

    def __init__(self, content):
        self.content = content
        print self.content  # this works

    exposed = True

    def GET(self):
        cherrypy.response.headers['Content-Type'] = 'application/json'
        return simplejson.dumps(self.content)

    def POST(self):
        self.content = simplejson.loads(cherrypy.request.body.read())

Irgendwelche Ideen?

Charles R.
quelle
Ich habe eine abgespeckte Version eines Beispiels direkt aus der Dokumentation verwendet .
Charles R
Mein Kommentar bleibt bestehen - CherryPy ruft keine Klassenmethoden __init__mit einem contentArgument auf (und beansprucht dies nicht in dem von Ihnen angegebenen Link). In dem detaillierten Beispiel, das sie haben, gibt der Benutzer den Code an, der aufruft, __init__und liefert die Argumente, die wir hier nicht gesehen haben, sodass ich keine Ahnung habe, in welchem ​​Zustand sich Ihr Objekt befindet, wenn Ihr # this worksKommentar relevant ist.
Nick Bastin
1
Fragen Sie nach der Zeile, in der die Instanz erstellt wird?
Charles R
Ja, ich habe versucht, Ihr Beispiel zu starten, um es zu testen, und ich war mir nicht sicher, wie Sie es instanziiert haben.
Nick Bastin
Der Code hat sich geändert. Ich erstelle es jetzt ohne das zusätzliche Argument. cherrypy.quickstart(Root(), '/', conf).
Charles R

Antworten:

1047

Ab Requests Version 2.4.2 können Sie alternativ den Parameter 'json' im Aufruf verwenden, um dies zu vereinfachen.

>>> import requests
>>> r = requests.post('http://httpbin.org/post', json={"key": "value"})
>>> r.status_code
200
>>> r.json()
{'args': {},
 'data': '{"key": "value"}',
 'files': {},
 'form': {},
 'headers': {'Accept': '*/*',
             'Accept-Encoding': 'gzip, deflate',
             'Connection': 'close',
             'Content-Length': '16',
             'Content-Type': 'application/json',
             'Host': 'httpbin.org',
             'User-Agent': 'python-requests/2.4.3 CPython/3.4.0',
             'X-Request-Id': 'xx-xx-xx'},
 'json': {'key': 'value'},
 'origin': 'x.x.x.x',
 'url': 'http://httpbin.org/post'}

BEARBEITEN: Diese Funktion wurde der offiziellen Dokumentation hinzugefügt. Sie können es hier anzeigen: Anforderungsdokumentation

Zeyang Lin
quelle
114
Ich kann nicht glauben, wie viel Zeit ich verschwendet habe, bevor ich auf Ihre Antwort gestoßen bin. Die Anforderungsdokumente müssen aktualisiert werden, der jsonParameter enthält absolut nichts . Ich musste nach Github, bevor ich eine Erwähnung sah: github.com/kennethreitz/requests/blob/…
IAmKale
1
Setzen Sie dies auf die akzeptierte Antwort, da dies ab 2.4.2 idiomatischer ist. Denken Sie daran, dass dies bei verrücktem Unicode möglicherweise nicht funktioniert.
Charles R
Ich war in den gleichen Schuhen wie @IAmKale. Dies hat die Kopfschmerzen, die ich mit dem API-Gateway von AWS hatte, erheblich gelindert. Es erfordert standardmäßig die POST-Daten im JSON-Format.
jstudios
1
Wie ein Idiot habe ich versucht, den Datenparameter mit application / json dem Inhaltstyp zu verwenden :(
Illegal Operator
Ich habe ein Beispiel dafür gesehen, das das dict-Objekt genommen und vor dem Senden json.dumps (Objekt) ausgeführt hat. Tun Sie das nicht ... es bringt Ihren JSON durcheinander. Das obige ist perfekt. Sie können ihm ein Python-Objekt übergeben und es wird zu perfektem JSON.
MydKnight
376

Es stellte sich heraus, dass mir die Header-Informationen fehlten. Folgendes funktioniert:

url = "http://localhost:8080"
data = {'sender': 'Alice', 'receiver': 'Bob', 'message': 'We did it!'}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
r = requests.post(url, data=json.dumps(data), headers=headers)
Charles R.
quelle
Guter Fang - ich sah Ihr application/jsonin GETund irgendwie verpasst , dass man es nicht zur Verfügung gestellt hatte auf den Antrag. Möglicherweise müssen Sie auch sicherstellen, dass Sie etwas von zurückgeben, POSToder Sie erhalten möglicherweise eine 500.
Nick Bastin
Scheint nicht notwendig zu sein. Wenn ich drucke r, bekomme ich <Response [200]>.
Charles R
Wie rufe ich diesen JSON auf der Serverseite ab?
VaidAbhishek
r = request.get (' localhost: 8080' ) c = r.content result = simplejson.loads (c)
Charles R
1
Kleine Köpfe hoch vor dem Gebrauch json.dumpshier. Der dataParameter von requestsfunktioniert gut mit Wörterbüchern. Keine Konvertierung in einen String erforderlich.
Advait S
71

Ab den Anforderungen 2.4.2 ( https://pypi.python.org/pypi/requests ) wird der Parameter "json" unterstützt. Es ist nicht erforderlich, "Inhaltstyp" anzugeben. Also die kürzere Version:

requests.post('http://httpbin.org/post', json={'test': 'cheers'})
ZZY
quelle
29

Der bessere Weg ist:

url = "http://xxx.xxxx.xx"

datas = {"cardno":"6248889874650987","systemIdentify":"s08","sourceChannel": 12}

headers = {'Content-type': 'application/json'}

rsp = requests.post(url, json=datas, headers=headers)
Ellen
quelle
18
das Content-type: application/jsonist überflüssig wie das json=schon andeutet.
Moshe
1
@ Moshe stimme voll und ganz zu, aber um eine neuere Version von Elasticsearch Content-type
Server anzufordern
@ Moshe, was ist, wenn der Inhaltstyp ist text/html; charset=UTF-8. Dann funktioniert oben nicht?
Anu
2
" Der bessere Weg ist ", 3 Jahre nach einer korrekten Antwort keine falschen Antworten zu posten . -1
CONvid19
3

Funktioniert perfekt mit Python 3.5+

Klient:

import requests
data = {'sender':   'Alice',
    'receiver': 'Bob',
    'message':  'We did it!'}
r = requests.post("http://localhost:8080", json={'json_payload': data})

Server:

class Root(object):

    def __init__(self, content):
        self.content = content
        print self.content  # this works

    exposed = True

    def GET(self):
        cherrypy.response.headers['Content-Type'] = 'application/json'
        return simplejson.dumps(self.content)

    @cherrypy.tools.json_in()
    @cherrypy.tools.json_out()
    def POST(self):
        self.content = cherrypy.request.json
        return {'status': 'success', 'message': 'updated'}
Ruhil Jaiswal
quelle
3

Welcher Parameter zwischen (data / json / files) verwendet werden soll, hängt tatsächlich von einem Anforderungsheader mit dem Namen ContentType ab (überprüfen Sie dies normalerweise über die Entwicklertools Ihres Browsers).

Wenn der Inhaltstyp application / x-www-form-urlencoded ist, sollte der Code wie folgt lauten:

requests.post(url, data=jsonObj)

Wenn der Inhaltstyp application / json ist, sollte Ihr Code einer der folgenden sein:

requests.post(url, json=jsonObj)
requests.post(url, data=jsonstr, headers={"Content-Type":"application/json"})

Wenn der Inhaltstyp mehrteilig / Formulardaten ist, wird er zum Hochladen von Dateien verwendet. Ihr Code sollte also wie folgt lauten:

requests.post(url, files=xxxx)
Xiaoming
quelle
Jesus Christus, danke. Ich habe mir vor ein paar Augenblicken die Haare ausgepeitscht.
Vahagn Tumanyan
froh, dass dir das helfen kann
:)