Publicar JSON utilizando solicitudes de Python

633

Necesito PUBLICAR un JSON de un cliente a un servidor. Estoy usando Python 2.7.1 y simplejson. El cliente está utilizando solicitudes. El servidor es CherryPy. Puedo OBTENER un JSON codificado desde el servidor (no se muestra el código), pero cuando intento PUBLICAR un JSON en el servidor, obtengo "400 Bad Request".

Aquí está mi 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)

Aquí está el código del 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())

¿Algunas ideas?

Charles R
fuente
Estaba usando una versión simplificada de un ejemplo directamente de la documentación .
Charles R
Mi comentario sigue en pie: CherryPy no llama a los __init__métodos de clase con un contentargumento (y no afirma en el enlace que proporciona). En el ejemplo detallado que tienen, el usuario proporciona el código que llama __init__y proporciona los argumentos, que no hemos visto aquí, así que no tengo idea de en qué estado se encuentra su objeto cuando su # this workscomentario es relevante.
Nick Bastin
1
¿Está pidiendo ver la línea donde se crea la instancia?
Charles R
Sí, estaba tratando de poner en marcha tu ejemplo para probarlo, y no estaba seguro de cómo lo estabas creando.
Nick Bastin
El código ha cambiado. Ahora lo estoy creando sin el argumento adicional. cherrypy.quickstart(Root(), '/', conf).
Charles R

Respuestas:

1053

A partir de la versión 2.4.2 de Solicitudes en adelante, también puede usar el parámetro 'json' en la llamada, lo que lo hace más 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'}

EDITAR: Esta característica se ha agregado a la documentación oficial. Puede verlo aquí: Solicitud de documentación

Zeyang Lin
fuente
114
No puedo creer cuánto tiempo perdí antes de tropezar con tu respuesta. Los documentos de solicitudes deben actualizarse, no hay absolutamente nada en el jsonparámetro. Tuve que ir a Github antes de ver alguna mención al respecto: github.com/kennethreitz/requests/blob/…
IAmKale
1
Establecer esto en la respuesta aceptada ya que es más idiomático a partir de 2.4.2. Tenga en cuenta que para unicode loco, esto puede no funcionar.
Charles R
Estaba en los mismos zapatos que @IAmKale. Esto ha aliviado bastante el dolor de cabeza que tenía con la API Gateway de AWS. Requiere los datos POST en formato JSON de forma predeterminada.
jstudios
1
Como un tonto, intenté usar el parámetro de datos con application / json, el tipo de contenido :(
Operador ilegal
Vi un ejemplo de esto que tomó el objeto dict y realizó json.dumps (objeto) antes de enviarlo. No hagas esto ... arruina tu JSON. Lo anterior es perfecto ... puedes pasarle un objeto python y se convierte en json perfecto.
MydKnight
376

Resulta que me faltaba la información del encabezado. Los siguientes trabajos:

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
fuente
Buena captura - Vi tu application/jsonen GETy de alguna manera echado de menos que no había proporcionado en la solicitud. También es posible que deba asegurarse de que devuelve algo POSTo podría recibir un 500.
Nick Bastin
No parece ser necesario. Cuando imprimo r, me sale <Response [200]>.
Charles R
¿Cómo recupero este json en el lado del servidor?
VaidAbhishek
r = request.get (' localhost: 8080' ) c = r.content result = simplejson.loads (c)
Charles R
1
Pequeñas cabezas antes de usar json.dumpsaquí. El dataparámetro de requestsfunciona bien con los diccionarios. No es necesario convertir a una cadena.
Avance S
71

Desde las solicitudes 2.4.2 ( https://pypi.python.org/pypi/requests ), se admite el parámetro "json". No es necesario especificar "Content-Type". Entonces la versión más corta:

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

La mejor manera es:

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
fuente
18
El Content-type: application/jsones redundante como el json=ya insinúa eso.
Moshe
1
@Moshe totalmente de acuerdo, pero para solicitar una versión más nueva Elasticsearch Sever requieren la configuración Content-type
devesh
@Moshe, ¿y si el tipo de contenido es text/html; charset=UTF-8? ¿Entonces arriba no funcionará?
Anu
2
" La mejor manera es " no publicar respuestas INCORRECTAS 3 años después de una respuesta correcta. -1
CONvid19
3

Funciona perfectamente con 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
fuente
3

El parámetro entre (datos / json / archivos) debe usarse, en realidad depende de un encabezado de solicitud llamado ContentType (generalmente verifique esto a través de las herramientas de desarrollador de su navegador),

cuando Content-Type es application / x-www-form-urlencoded, el código debe ser:

requests.post(url, data=jsonObj)

cuando Content-Type es application / json, se supone que su código es uno de los siguientes:

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

cuando Content-Type es multipart / form-data, se usa para cargar archivos, por lo que su código debe ser:

requests.post(url, files=xxxx)
Xiaoming
fuente
Jesucristo, gracias. Me estaba arrancando el pelo hace unos momentos.
Vahagn Tumanyan
Me