How to handle OPTIONS request in Cherrypy?

482 views
Skip to first unread message

Edvin Beqari

unread,
Jun 16, 2019, 7:01:29 PM6/16/19
to cherrypy-users
I am trying to make a POST request with Axios to a Cherrypy server but I am running into Cors issues. 

Axios client: 

try {
const data = JSON.stringify({
name: "Bob"
});

const request = await ServerApi.post('/catalog/input', data, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});

} catch (e) {
console.log(`Failed to send inputs: ${e}`);
}

Cherrypy server:

import os
import os.path
import random
import string
import json
import cherrypy
from controller import Controller

class Server(object):

@cherrypy.expose
def index(self):
return open('../cococlient/build/index.html')

def CORS():
if cherrypy.request.method == 'OPTIONS':
cherrypy.response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS'
cherrypy.response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Access-Control-Allow-Headers'
cherrypy.response.headers['Access-Control-Allow-Origin'] = 'http://127.0.0.1:3000'
cherrypy.response.headers['Access-Control-Max-Age'] = '3600'
return True
else:
cherrypy.response.headers['Access-Control-Allow-Origin'] = 'http://127.0.0.1:3000'

if __name__ == '__main__':
conf = {
'/': {
'tools.staticdir.root': os.path.abspath(os.getcwd())
},
'/catalog': {
'tools.CORS.on': True,
'tools.response_headers.on': True
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': '../cococlient/build/static'
}
}

server = Server()
server.catalog = Controller()
cherrypy.tools.CORS = cherrypy.Tool('before_handler', CORS)

cherrypy.quickstart(server, '/', conf)

And finally the Controller class with the input method:

import cherrypy
import json
import simplejson
from cherrypy import tools
from catalog import Catalog

class Controller(object):

@cherrypy.expose
@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
def input(self):
data = cherrypy.request.json
return data.items()

So what happens is the POST method gets turned into OPTIONS which does not include the payload;
and consequently the input method raises an error which says "cherrypy.request" does not have an attribute "json."

So, how do I configure Cherrypy to return the pre-flight response headers before calling the input method.

I am not sure how to proceed. Thank you.


Edvin Beqari

unread,
Jun 16, 2019, 9:57:10 PM6/16/19
to cherrypy-users
I fixed the problem by changing the cors method with the one shown below which I found online. It would be nice if someone can explain what sort of majic is going on here .i.e.: what is a pahe handler in this case or whatever. 

def cors_tool():
'''
Handle both simple and complex CORS requests
Add CORS headers to each response. If the request is a CORS preflight
request swap out the default handler with a simple, single-purpose handler
that verifies the request and provides a valid CORS response.
'''
req_head = cherrypy.request.headers
resp_head = cherrypy.response.headers

# Always set response headers necessary for 'simple' CORS.
resp_head['Access-Control-Allow-Origin'] = req_head.get('Origin', '*')
resp_head['Access-Control-Expose-Headers'] = 'GET, POST'
resp_head['Access-Control-Allow-Credentials'] = 'true'
# Non-simple CORS preflight request; short-circuit the normal handler.
if cherrypy.request.method == 'OPTIONS':
ac_method = req_head.get('Access-Control-Request-Method', None)
allowed_methods = ['GET', 'POST']
allowed_headers = [
'Content-Type',
'X-Auth-Token',
'X-Requested-With',
]
if ac_method and ac_method in allowed_methods:
resp_head['Access-Control-Allow-Methods'] = ', '.join(allowed_methods)
resp_head['Access-Control-Allow-Headers'] = ', '.join(allowed_headers)
resp_head['Connection'] = 'keep-alive'
resp_head['Access-Control-Max-Age'] = '3600'
# CORS requests should short-circuit the other tools.
cherrypy.response.body = ''.encode('utf8')
cherrypy.response.status = 200
cherrypy.serving.request.handler = None
# Needed to avoid the auth_tool check.
if cherrypy.request.config.get('tools.sessions.on', False):
cherrypy.session['token'] = True
return True
Reply all
Reply to author
Forward
0 new messages