Reputation: 3148
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.
Upvotes: 2
Views: 2014
Reputation: 718
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.
Upvotes: 0
Reputation: 25234
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)
Upvotes: 2