59

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.

daaawx
  • 3,273
  • 2
  • 17
  • 16
Jaroslav Klimčík
  • 4,548
  • 12
  • 39
  • 58

7 Answers7

95

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

metakermit
  • 21,267
  • 15
  • 86
  • 95
campervancoder
  • 1,579
  • 2
  • 11
  • 15
61

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()
Luc Gendrot
  • 806
  • 9
  • 11
37

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
Pro Q
  • 4,391
  • 4
  • 43
  • 92
Zachary Jacobi
  • 1,073
  • 10
  • 26
  • 2
    "NameError: name 'crossdomain' is not defined". What should I import to fix it? – Jaroslav Klimčík Mar 04 '14 at 20:47
  • 2
    You need to copy and paste the decorator from the link into your Flask application. Once that's in there, crossdomain will be defined and everything will work. – Zachary Jacobi Mar 04 '14 at 21:03
  • 13
    Important note for Python 3.x users: The code for the decorator which is linked to in the post does not work for Python 3.x. It gives "NameError: name 'basestring' is not defined" error because basestring is not available in Python 3.x. If you change the code by using "list" instead of "basestring", it works. – Zafer Jan 02 '17 at 11:28
  • 1
    The same decorator (or almost the same, didn’t diff) can be found [here](http://flask.pocoo.org/snippets/56/), with more documentation and comments. Besides, why do you add options method? – Ulysse BN Feb 03 '17 at 23:43
  • The link is down. Please post the decorator you used. – Syntactic Fructose Apr 02 '17 at 19:45
  • @UlysseBN "OPTIONS" is required by the HTTP standard for CORS compliancy when the request is "complex enough" (which is pretty much anything except a GET...) – Juha Untinen May 06 '17 at 21:37
  • current_app, make_response, update_wrapper,timedelta also undefined! – Kanerva Peter Jun 13 '18 at 16:24
23

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)
Alok Patra
  • 409
  • 6
  • 13
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.

boxmein
  • 865
  • 7
  • 19
2

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.

Vikas
  • 1,900
  • 1
  • 19
  • 20
0

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.

Oscar Paz
  • 18,084
  • 3
  • 27
  • 42