Publier JSON à l'aide de requêtes Python

633

J'ai besoin de POSTER un JSON d'un client vers un serveur. J'utilise Python 2.7.1 et simplejson. Le client utilise des demandes. Le serveur est CherryPy. Je peux obtenir un JSON codé en dur à partir du serveur (code non illustré), mais lorsque j'essaye de POSTER un JSON sur le serveur, j'obtiens "400 Bad Request".

Voici mon code client:

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)

Voici le code du serveur.

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())

Des idées?

Charles R
la source
J'utilisais une version simplifiée d'un exemple tout droit sorti de la documentation .
Charles R
Mon commentaire est toujours valable - CherryPy n'appelle pas les __init__méthodes de classe avec un contentargument (et ne prétend pas le faire dans le lien que vous fournissez). Dans l'exemple détaillé qu'ils ont, l'utilisateur fournit le code qui appelle __init__et fournit les arguments, que nous n'avons pas vu ici, donc je n'ai aucune idée de l'état dans lequel se trouve votre objet lorsque votre # this workscommentaire est pertinent.
Nick Bastin
1
Demandez-vous à voir la ligne où l'instance est créée?
Charles R
oui, j'essayais de démarrer votre exemple pour le tester, et je ne savais pas comment vous l'instanciez.
Nick Bastin
Le code a changé. Je le crée maintenant sans l'argument supplémentaire. cherrypy.quickstart(Root(), '/', conf).
Charles R

Réponses:

1053

À partir de Requests version 2.4.2 et suivantes, vous pouvez également utiliser le paramètre 'json' dans l'appel, ce qui le rend plus simple.

>>> 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'}

EDIT: Cette fonctionnalité a été ajoutée à la documentation officielle. Vous pouvez le consulter ici: Documentation des demandes

Zeyang Lin
la source
114
Je ne peux pas croire combien de temps j'ai perdu avant de tomber sur votre réponse. Les demandes de documents doivent être mises à jour, il n'y a absolument rien sur le jsonparamètre. Je devais aller dans Github avant d'en voir la moindre mention: github.com/kennethreitz/requests/blob/…
IAmKale
1
Mettre cela à la réponse acceptée car c'est plus idiomatique à partir de 2.4.2. Gardez à l'esprit, pour unicode fou, cela peut ne pas fonctionner.
Charles R du
J'étais dans la même peau que @IAmKale. Cela a soulagé tout le mal de tête que j'avais avec la passerelle API d'AWS. Il nécessite par défaut les données POST au format JSON.
jstudios
1
Comme un idiot, j'ai essayé d'utiliser le paramètre de données avec application / json le type de contenu :(
Illegal Operator
J'ai vu un exemple de cela qui a pris l'objet dict et effectué json.dumps (object) avant d'envoyer. Ne faites pas ça ... ça gâche votre JSON. Ce qui précède est parfait ... vous pouvez lui passer un objet python et il se transforme en json parfait.
MydKnight
376

Il s'avère que je manquais les informations d'en-tête. Les oeuvres suivantes:

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
la source
Bonne prise - J'ai vu votre application/jsondans GETet en quelque sorte manqué que vous aviez pas fourni à la demande. Vous devrez peut-être également vous assurer que vous retournez quelque chose POSTou vous pourriez en obtenir un 500.
Nick Bastin
Cela ne semble pas nécessaire. Quand j'imprime r, je reçois <Response [200]>.
Charles R
Comment puis-je récupérer ce json côté serveur?
VaidAbhishek
r = requests.get (' localhost: 8080' ) c = r.content result = simplejson.loads (c)
Charles R
1
Attention avant d'utiliser json.dumpsici. Le dataparamètre de requestsfonctionne bien avec les dictionnaires. Pas besoin de convertir en chaîne.
Advait S
71

À partir des requêtes 2.4.2 ( https://pypi.python.org/pypi/requests ), le paramètre "json" est pris en charge. Pas besoin de spécifier "Content-Type". Donc, la version courte:

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

La meilleure façon est:

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
la source
18
le Content-type: application/jsonest redondant comme l' json=indique déjà cela.
Moshe
1
@Moshe est tout à fait d'accord, mais pour demander une version plus récente, le serveur Elasticsearch doit être défini Content-type
devesh
@Moshe, que faire si le type de contenu est text/html; charset=UTF-8. Alors ci-dessus ne fonctionnera pas?
Anu
2
" La meilleure façon est " de ne pas poster de mauvaises réponses 3 ans après une réponse correcte. -1
CONvid19
3

Fonctionne parfaitement avec python 3.5+

client:

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

serveur:

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
la source
3

Quel paramètre entre (données / json / fichiers) doit être utilisé, cela dépend en fait d'un en-tête de requête nommé ContentType (vérifiez généralement cela via les outils de développement de votre navigateur),

lorsque le Content-Type est application / x-www-form-urlencoded, le code doit être:

requests.post(url, data=jsonObj)

lorsque le Content-Type est application / json, votre code est censé être l'un des suivants:

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

lorsque le Content-Type est multipart / form-data, il est utilisé pour télécharger des fichiers, donc votre code doit être:

requests.post(url, files=xxxx)
xiaoming
la source
Jésus-Christ, merci. Il y a quelques instants, je me déchaînais les cheveux.
Vahagn Tumanyan
heureux que cela puisse vous aider
:)