简体   繁体   中英

Sending JSON string to cherrypy

I'm trying to send a JSON string from a single HTML (this file is not served by cherrypy) file via Javascript to a cherrpy server.

This is my minimal cherrypy example ( followed the "dealing with json" part )

import cherrypy

class HelloJson(object):
    @cherrypy.expose
    @cherrypy.tools.json_in()
    def default(self):
        data = cherrypy.request.json
        print(data)
        return "Hello world!"

if __name__ == '__main__':
    cherrypy.config.update({'server.socket_port':1234})
    cherrypy.quickstart(HelloJson())

Sending a JSON string via python works gently

>>> requests.post('http://localhost:1234', json=json.dumps({'Hello': 'Json'}))
<Response [200]>
>>> 

The cherrypy output prints the json string too

20:59 $ ./HelloJson.py 
[24/Aug/2015:20:59:34] ENGINE Listening for SIGTERM.
[24/Aug/2015:20:59:34] ENGINE Listening for SIGUSR1.
[24/Aug/2015:20:59:34] ENGINE Listening for SIGHUP.
[24/Aug/2015:20:59:34] ENGINE Bus STARTING
CherryPy Checker:
The Application mounted at '' has an empty config.

[24/Aug/2015:20:59:34] ENGINE Started monitor thread '_TimeoutMonitor'.
[24/Aug/2015:20:59:34] ENGINE Started monitor thread 'Autoreloader'.
[24/Aug/2015:20:59:34] ENGINE Serving on http://127.0.0.1:1234
[24/Aug/2015:20:59:34] ENGINE Bus STARTED
{"Hello": "Json"}
127.0.0.1 - - [24/Aug/2015:21:00:17] "POST / HTTP/1.1" 200 12 "" "python-requests/2.7.0 CPython/3.4.3 Linux/4.1.5-1-ARCH"

So my single HTML file looks like this

<html>
<head>
<script>
function makeRequest()
{
    var insertJSON = { "my_key": "my_value" };

    var xmlhttp = new XMLHttpRequest();   // new HttpRequest instance 
    xmlhttp.open("POST", "http://localhost:1234");
    xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
    xmlhttp.send(JSON.stringify(insertJSON));

}
</script>
</head>
<body>
<form name="frm1" id="yourTextBox" onsubmit="makeRequest()">
<input type="submit" value="Submit">
</form>
</body>
</html>

But this results in an error AttributeError: 'Request' object has no attribute 'json'

[24/Aug/2015:21:10:36] HTTP 
Request Headers:
  CONNECTION: keep-alive
  ACCEPT-LANGUAGE: en-US,en;q=0.5
  ACCESS-CONTROL-REQUEST-HEADERS: content-type
  ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  USER-AGENT: Mozilla/5.0 (X11; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0
  ACCESS-CONTROL-REQUEST-METHOD: POST
  ACCEPT-ENCODING: gzip, deflate
  PRAGMA: no-cache
  CACHE-CONTROL: no-cache
  HOST: localhost:1234
  Remote-Addr: 127.0.0.1
  ORIGIN: null
[24/Aug/2015:21:10:36] HTTP Traceback (most recent call last):
  File "/usr/lib/python3.4/site-packages/cherrypy/_cprequest.py", line 670, in respond
    response.body = self.handler()
  File "/usr/lib/python3.4/site-packages/cherrypy/lib/encoding.py", line 217, in __call__
    self.body = self.oldhandler(*args, **kwargs)
  File "/usr/lib/python3.4/site-packages/cherrypy/_cpdispatch.py", line 61, in __call__
    return self.callable(*self.args, **self.kwargs)
  File "./HelloJson.py", line 15, in default
    data = cherrypy.request.json
  File "/usr/lib/python3.4/site-packages/cherrypy/__init__.py", line 224, in __getattr__
    return getattr(child, name)
AttributeError: 'Request' object has no attribute 'json'

127.0.0.1 - - [24/Aug/2015:21:10:36] "OPTIONS / HTTP/1.1" 500 1515 "" "Mozilla/5.0 (X11; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0"

I've no idea what I'm doing wrong.

The OPTIONS request you see is a CORS preflight request , which obviously isn't a JSON request and you see the error. Because you open your file from file:// protocol (or another host), and CherryPy is serving on http://127.0.0.1:1234 you do a cross-domain request, which is subject to Same-Origin Policy .

The simplest way to solve this is to also serve the HTML file by CherryPy ( Static content serving ). The hard way is to provide proper CORS headers to allow cross domain requests (see this answer )

I agree with saaj's response that the browser sends a CORS preflight request which can be handled in the following manner:

import cherrypy

class HelloJson(object):
    @cherrypy.expose
    @cherrypy.tools.json_in()
    def POST(self):
        data = cherrypy.request.json
        print(data)
        return "Hello world!"

    def OPTIONS(self):
        cherrypy.response.headers["Access-Control-Allow-Methods"] = "POST, OPTIONS"
        cherrypy.response.headers["Access-Control-Allow-Credentials"] = "true"
        cherrypy.response.headers["Access-Control-Max-Age"] = "86400"
        cherrypy.response.headers[
            "Access-Control-Allow-Headers"] = "X-Mobile, Authorization, Origin, X-Requested-With, Content-Type, Accept"
        cherrypy.response.headers["Content-Type"] = "application/json; charset=utf-8"
        return ''


if __name__ == '__main__':
    cherrypy.config.update({
            'server.socket_port':1234, 
            'request.dispatch': cherrypy.dispatch.MethodDispatcher()
    })
    cherrypy.quickstart(HelloJson(), '/')

This would work because now you have enabled your API backend to listen to browser's OPTIONS call which tells the browser, what are the allowed methods and origins. cherrypy.dispatch.MethodDispatcher() enables you to treat your class as a method dispatcher and thus you can use the class to serve RESTFUL API.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM