I'm trying to build a Cherrypy/Python webservice. I already spend the whole day on finding out how to make a cross domain ajax request possible. That's finally working, but now I have the next issue. I think I already know the solution, but I don't know how to implement it. The problem is that when I'm sending the ajax request, the Cherrypy server responds with:
415 Unsupported Media Type
Expected an entity of content type application/json, text/javascript
Traceback (most recent call last): File "/Library/Python/2.7/site-packages/cherrypy/_cprequest.py", line 663, in respond self.body.process() File "/Library/Python/2.7/site-packages/cherrypy/_cpreqbody.py", line 996, in process super(RequestBody, self).process() File "/Library/Python/2.7/site-packages/cherrypy/_cpreqbody.py", line 538, in process self.default_proc() File "/Library/Python/2.7/site-packages/cherrypy/_cperror.py", line 411, in __call__ raise selfHTTPError: (415, u'Expected an entity of content type application/json, text/javascript')
The solution I found, and trying to test, is adding this line to the configuration:
'tools.json_in.force': False
So I tried to implement it in this code:
import cherrypy
import json
import sys
class RelatedDocuments:
def index(self):
return "Hello World!"
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
def findRelated(self, **raw):
#Get JSON message form request
request = cherrypy.request.json
result = []
#SOME CODE...
return result;
# Expose the index method through the web. CherryPy will never
# publish methods that don't have the exposed attribute set to True.
index.exposed = True
findRelated.exposed = True
def CORS():
cherrypy.response.headers["Access-Control-Allow-Origin"] = "*"
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'webserver.conf')
config = {
'global': {
'server.socket_host':'127.0.0.1',
'server.socket_port': 8080,
'log.error_file' : 'Web.log',
'log.access_file' : 'Access.log'
},
'/': {
'tools.CORS.on': True
}
}
if __name__ == '__main__':
cherrypy.tools.CORS = cherrypy.Tool('before_finalize', CORS)
cherrypy.quickstart(RelatedDocuments(),config=config)
I added the config line under the tools.CORS.on line, but that didn't work. Next i tried this:
cherrypy.config.update({
'tools.json_in.force': False,
});
Didn't work eiter..next I tried to implement this right above the findRelated method:
@cherrypy.config(**{'tools.json_in.force': False})
All of the implementations gave me a 500 error, I really appreciate it if somebody can help me. Thanks in advance!
In general if you have chosen a tool, then you're better using it, instead of fighting it. CherryPy tells you that for JSON input it expects request with
application/json
ortext/javascript
content-type.Here's code of
cherrypy.lib.jsontools.json_in
:force
doesn't do anything more than removing existing body processors. If you setforce
toFalse
, then you need to tell CherryPy how to treat the request body you send to it.Or, better, use CherryPy and tell it correct content-type. With jQuery it's as simple as:
I've realised that the question is in fact about CORS preflight request. CORS specification defines the following condition for a simple CORS request:
GET
,HEAD
,POST
Accept
,Accept-Language
,Content-Language
,Content-Type
application/x-www-form-urlencoded
,multipart/form-data
,text/plain
Otherwise CORS request isn't simple, and use preflight OPTIONS request before actual request to ensure it's eligible. Here is good CORS how-to.
So if you want to keep things simple you may want to revert to normal
application/x-www-form-urlencoded
. Otherwise you need to handle preflight requests correctly. Here's working example (don't forget to addlocalhost
alias).