Postar JSON usando solicitações Python

633

Eu preciso postar um JSON de um cliente para um servidor. Estou usando o Python 2.7.1 e simplejson. O cliente está usando solicitações. O servidor é CherryPy. Posso OBTER um JSON codificado no servidor (código não mostrado), mas quando tento POSTAR um JSON para o servidor, recebo "400 Bad Request".

Aqui está o meu código de cliente:

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)

Aqui está o código do servidor.

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

Alguma ideia?

Charles R
fonte
Eu estava usando uma versão simplificada de um exemplo direto da documentação .
Charles R
Meu comentário ainda permanece - CherryPy não chama __init__métodos de classe com um contentargumento (e não afirma no link que você forneceu). No exemplo detalhado que eles têm, o usuário fornece o código que chama __init__e fornece os argumentos, que não vimos aqui, então não faço ideia do estado do seu objeto quando o seu # this workscomentário é relevante.
Nick Bastin
1
Você está pedindo para ver a linha em que a instância é criada?
Charles R:
Sim, eu estava tentando iniciar o seu exemplo para testá-lo e não tinha certeza de como você o instanciava.
Nick Bastin 31/03
O código foi alterado. Agora estou criando sem o argumento extra. cherrypy.quickstart(Root(), '/', conf).
Charles R

Respostas:

1053

A partir da versão 2.4.2 e posteriores, você pode usar o parâmetro 'json' como alternativa na chamada, o que simplifica.

>>> 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: Este recurso foi adicionado à documentação oficial. Você pode visualizá-lo aqui: Solicita a documentação

Zeyang Lin
fonte
114
Não acredito quanto tempo perdi antes de me deparar com sua resposta. Os documentos de solicitações precisam ser atualizados, não há absolutamente nada no jsonparâmetro. Eu tive que entrar no Github antes de ver qualquer menção a ele: github.com/kennethreitz/requests/blob/…
IAmKale
1
Configurando isso para a resposta aceita, já que isso é mais idiomático a partir do 2.4.2. Lembre-se, para unicode louco, isso pode não funcionar.
Charles R
Eu estava no mesmo lugar que @IAmKale. Isso aliviou bastante a dor de cabeça que eu estava tendo com o API Gateway da AWS. Requer os dados POST no formato JSON por padrão.
jstudios
1
Como um tolo, tentei usar o parâmetro data com application / json, o tipo de conteúdo :(
Operador ilegal
Eu vi um exemplo disso que pegou o objeto dict e executou json.dumps (object) antes de enviar. Não faça isso ... isso atrapalha seu JSON. O exemplo acima é perfeito ... você pode passar para ele um objeto python e ele se transforma em json perfeito.
MydKnight
376

Acontece que eu estava perdendo as informações do cabeçalho. Os seguintes trabalhos:

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
fonte
Boa captura - Eu vi seu application/jsonem GETe de alguma forma perdeu que não lhe tinha fornecido a pedido. Você também pode precisar se certificar de retornar algo POSTou obter um 500.
Nick Bastin 31/03
Não parece ser necessário. Quando imprimo r, recebo <Response [200]>.
Charles R
Como recupero esse json no lado do servidor?
VaidAbhishek
r = requests.get (' localhost: 8080' ) C = r.content resultar = simplejson.loads (c)
Charles R
1
Cabeças pequenas antes de usar json.dumpsaqui. O dataparâmetro requestsfunciona bem com dicionários. Não há necessidade de converter em uma string.
Advait S
71

Nas solicitações 2.4.2 ( https://pypi.python.org/pypi/requests ), o parâmetro "json" é suportado. Não há necessidade de especificar "Tipo de Conteúdo". Portanto, a versão mais curta:

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

A melhor maneira é:

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
fonte
18
o Content-type: application/jsoné redundante como o json=já sugere isso.
Moshe
1
@Moshe concordo totalmente, mas para solicitar mais recente versão ElasticSearch Sever exigem a set Content-type
devesh
@ Moshe, e se o tipo de conteúdo for text/html; charset=UTF-8. Então acima não vai funcionar?
Anu
2
" A melhor maneira é " não publicar respostas ERRADAS 3 anos após uma resposta correta. -1
CONvid19
3

Funciona perfeitamente com python 3.5+

cliente:

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

servidor:

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
fonte
3

Qual parâmetro entre (data / json / files) deve ser usado, na verdade depende de um cabeçalho de solicitação chamado ContentType (geralmente verifique isso nas ferramentas de desenvolvedor do seu navegador),

quando o Tipo de conteúdo é application / x-www-form-urlencoded, o código deve ser:

requests.post(url, data=jsonObj)

quando o Content-Type é application / json, seu código deve ser um dos seguintes:

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

quando o Content-Type é multipart / form-data, é usado para fazer upload de arquivos; portanto, seu código deve ser:

requests.post(url, files=xxxx)
xiaoming
fonte
Jesus Cristo, obrigado. Eu estava puxando meu cabelo alguns momentos atrás.
Vahagn Tumanyan
feliz que pode ajudá-lo
:)