Question :
I need to send data through XmlHttpRequest
from JavaScript to Python server. Because I’m using localhost, I need to use CORS. I’m using the Flask framework and its module flask_cors
.
As JavaScript I have this:
var xmlhttp;
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("POST", "http://localhost:5000/signin", true);
var params = "email=" + email + "&password=" + password;
xmlhttp.onreadystatechange = function() {//Call a function when the state changes.
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
alert(xmlhttp.responseText);
}
}
xmlhttp.send(params);
and Python code:
@app.route('/signin', methods=['POST'])
@cross_origin()
def sign_in():
email = cgi.escape(request.values["email"])
password = cgi.escape(request.values["password"])
But when I execute it I get this message:
XMLHttpRequest cannot load localhost:5000/signin. No
‘Access-Control-Allow-Origin’ header is present on the requested
resource. Origin ‘null’ is therefore not allowed access.
How should I fix it? I know that I need to use some “Access-Control-Allow-Origin” header but I don’t know how to implement it in this code. By the way I need to use pure JavaScript.
Answer #1:
I got Javascript working with Flask by using this decorator, and adding “OPTIONS” to my list of acceptable methods. The decorator should be used beneath your route decorator, like this:
@app.route('/login', methods=['POST', 'OPTIONS'])
@crossdomain(origin='*')
def login()
...
Edit:
Link appears to be broken. Here’s the decorator I used.
from datetime import timedelta
from flask import make_response, request, current_app
from functools import update_wrapper
def crossdomain(origin=None, methods=None, headers=None, max_age=21600,
attach_to_all=True, automatic_options=True):
"""Decorator function that allows crossdomain requests.
Courtesy of
https://blog.skyred.fi/articles/better-crossdomain-snippet-for-flask.html
"""
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
# use str instead of basestring if using Python 3.x
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
# use str instead of basestring if using Python 3.x
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
""" Determines which methods are allowed
"""
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
"""The decorator function
"""
def wrapped_function(*args, **kwargs):
"""Caries out the actual cross domain code
"""
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
h['Access-Control-Allow-Credentials'] = 'true'
h['Access-Control-Allow-Headers'] =
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
Answer #2:
I have used the flask-cors extension.
Install using pip install flask-cors
Then it’s simply
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
This will allow all domains
Answer #3:
Old question, but for future googlers with this problem, I solved it (and a few other downstream issues having to do with CORS) for my flask-restful app by adding the following to my app.py file:
app = Flask(__name__)
api = Api(app)
@app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
return response
if __name__ == '__main__':
app.run()
Answer #4:
When using python 2.7
app = Flask(__name__)
api = Api(app)
@app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
return response
if __name__ == '__main__':
app.run()
When running on python3 or ahead,
install flask-cors using the command pip install flask-cors
The add the following:
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
Answer #5:
There’s actually a brilliant snippet on the Flask site to modify the Access-Control-Allow-Origin
header server-side. http://flask.pocoo.org/snippets/56/
You have the easy way out from there, which is to allow every *
domain to access your URL, or specifying your selection of URLs inside the header.
From the MDN’s article on CORS:
In this case, the server responds with a
Access-Control-Allow-Origin: *
which means that the
resource can be accessed by any domain in a cross-site manner. If the resource owners at
http://bar.other wished to restrict access to the resource to be only from
http://foo.example, they would send back:
Access-Control-Allow-Origin: http://foo.example
.
Answer #6:
I have used the solution from Zachary. Works well.
For those who are wondering where to place the new decorator:
Just copy the code from the link that Zachary provided and place it in a .py
file
Place it in the folder where your python modules are present(varies based on what system you use and whether or not you are using a virtual environment).
In your flask app, import the method crossdomain
from the newly created python module and use it.
Answer #7:
The Access-Control-Allow-Origin must be sent by the server, not by you. When you make a call to another domain, the browser checks whether this header is returned by the server. If it isn’t, the call fails. I don’t know Python, so I don’t know how to make your server send this header, or even if you can modify the server at all.