Solve Cross Origin Resource Sharing with Flask

218,940

Solution 1

It worked like a champ, after bit modification to your code

# initialization
app = Flask(__name__)
app.config['SECRET_KEY'] = 'the quick brown fox jumps over the lazy   dog'
app.config['CORS_HEADERS'] = 'Content-Type'

cors = CORS(app, resources={r"/foo": {"origins": "http://localhost:port"}})

@app.route('/foo', methods=['POST'])
@cross_origin(origin='localhost',headers=['Content- Type','Authorization'])
def foo():
    return request.json['inputVar']

if __name__ == '__main__':
   app.run()

I replaced * by localhost. Since as I read in many blogs and posts, you should allow access for specific domain

Solution 2

You can get the results with a simple:

@app.route('your route', methods=['GET'])
def yourMethod(params):
    response = flask.jsonify({'some': 'data'})
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response

Solution 3

Well, I faced the same issue. For new users who may land at this page. Just follow their official documentation.

Install flask-cors

pip install -U flask-cors

then after app initialization, initialize flask-cors with default arguments:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
   return "Hello, cross-origin-world!"

Solution 4

Might as well make this an answer. I had the same issue today and it was more of a non-issue than expected. After adding the CORS functionality, you must restart your Flask server (ctrl + c -> python manage.py runserver, or whichever method you use)) in order for the change to take effect, even if the code is correct. Otherwise the CORS will not work in the active instance.

Here's how it looks like for me and it works (Python 3.6.1, Flask 0.12):

factory.py:

from flask import Flask
from flask_cors import CORS  # This is the magic


def create_app(register_stuffs=True):
    """Configure the app and views"""
    app = Flask(__name__)
    CORS(app)  # This makes the CORS feature cover all routes in the app

    if register_stuffs:
        register_views(app)
    return app


def register_views(app):
    """Setup the base routes for various features."""
    from backend.apps.api.views import ApiView
    ApiView.register(app, route_base="/api/v1.0/")

views.py:

from flask import jsonify
from flask_classy import FlaskView, route


class ApiView(FlaskView):
    @route("/", methods=["GET"])
    def index(self):
        return "API v1.0"

    @route("/stuff", methods=["GET", "POST"])
    def news(self):
        return jsonify({
            "stuff": "Here be stuff"
        })

In my React app console.log:

Sending request:
GET /stuff
With parameters:
null
bundle.js:17316 Received data from Api:
{"stuff": "Here be stuff"}

Solution 5

Note that setting the Access-Control-Allow-Origin header in the Flask response object is fine in many cases (such as this one), but it has no effect when serving static assets (in a production setup, at least). That's because static assets are served directly by the front-facing web server (usually Nginx or Apache). So, in that case, you have to set the response header at the web server level, not in Flask.

For more details, see this article that I wrote a while back, explaining how to set the headers (in my case, I was trying to do cross-domain serving of Font Awesome assets).

Also, as @Satu said, you may need to allow access only for a specific domain, in the case of JS AJAX requests. For requesting static assets (like font files), I think the rules are less strict, and allowing access for any domain is more accepted.

Share:
218,940
Matteo
Author by

Matteo

I'm a PhD student in Computer Science.

Updated on May 08, 2022

Comments

  • Matteo
    Matteo about 2 years

    For the following ajax post request for Flask (how can I use data posted from ajax in flask?):

    $.ajax({
        url: "http://127.0.0.1:5000/foo", 
        type: "POST",
        contentType: "application/json",
        data: JSON.stringify({'inputVar': 1}),
        success: function( data ) { 
            alert( "success" + data );
        }   
    });
    

    I get a Cross Origin Resource Sharing (CORS) error:

    No 'Access-Control-Allow-Origin' header is present on the requested resource. 
    Origin 'null' is therefore not allowed access. 
    The response had HTTP status code 500.
    

    I tried solving it in the two following ways, but none seems to work.

    1. Using Flask-CORS

    This is a Flask extension for handling CORS that should make cross-origin AJAX possible.

    My pythonServer.py using this solution:

    from flask import Flask
    from flask.ext.cors import CORS, cross_origin
    
    app = Flask(__name__)
    cors = CORS(app, resources={r"/foo": {"origins": "*"}})
    app.config['CORS_HEADERS'] = 'Content-Type'
    
    @app.route('/foo', methods=['POST','OPTIONS'])
    @cross_origin(origin='*',headers=['Content-Type','Authorization'])
    def foo():
        return request.json['inputVar']
    
    if __name__ == '__main__':
        app.run()
    
    1. Using specific Flask Decorator

    This is an official Flask code snippet defining a decorator that should allow CORS on the functions it decorates.

    My pythonServer.py using this solution:

    from flask import Flask, make_response, request, current_app
    from datetime import timedelta
    from functools import update_wrapper
    
    app = Flask(__name__)
    
    def crossdomain(origin=None, methods=None, headers=None,
                    max_age=21600, attach_to_all=True,
                    automatic_options=True):
        if methods is not None:
            methods = ', '.join(sorted(x.upper() for x in methods))
        if headers is not None and not isinstance(headers, basestring):
            headers = ', '.join(x.upper() for x in headers)
        if not isinstance(origin, basestring):
            origin = ', '.join(origin)
        if isinstance(max_age, timedelta):
            max_age = max_age.total_seconds()
    
        def get_methods():
            if methods is not None:
                return methods
    
            options_resp = current_app.make_default_options_response()
            return options_resp.headers['allow']
    
        def decorator(f):
            def wrapped_function(*args, **kwargs):
                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)
                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
    
    @app.route('/foo', methods=['GET','POST','OPTIONS'])
    @crossdomain(origin="*")
    def foo():
        return request.json['inputVar']
    
    if __name__ == '__main__':
        app.run()
    

    Can you please give some some indication of why that is?

    • shao
      shao over 9 years
      did you figure out? I run into the exact same issue :(
    • Juha Untinen
      Juha Untinen about 7 years
      This is an old question, but just to be sure: Did you restart your Flask server? I was also wondering why I got the same error even thought everything was exactly as they should. Turns out, you have to restart the server in order for it to really take effect
  • Cătălin Florescu
    Cătălin Florescu over 7 years
    Better that adding crossdomain implementation!
  • Anthony
    Anthony over 7 years
    Simple and effective
  • PALEN
    PALEN almost 7 years
    Brilliant! I agree, simple and effective. IMO Should be accepted answer
  • Matteo
    Matteo almost 7 years
    @Salvador Dali - Do you know what is proper way of allowing cross origin if I am rendering a template instead of just a json object? i.e. last line of code in yourMethod is: return render_template('template.html',some_var = response)
  • W.Leto
    W.Leto over 5 years
    Could this be used in a post method? I tried for a file upload method and I failed.
  • humblePilgrim
    humblePilgrim almost 5 years
    This gives the following error for me Access to XMLHttpRequest at 'my_domain' from origin '127.0.0.1:5000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '127.0.0.1:5000, *', but only one is allowed.
  • Eziz Durdyyev
    Eziz Durdyyev over 4 years
    why do you do the same thing(support_credentials=True) in cross_origin?
  • W.P. McNeill
    W.P. McNeill over 4 years
  • Jose
    Jose about 4 years
    Works for me too Tks!
  • BLang
    BLang about 4 years
    In case anyone is using blueprints, you need to add the CORS() to each blueprint, for example: my_blueprint = Blueprint('my_bp_name', name, url_prefix="/my-prefix") CORS(my_blueprint)
  • José Tomás Tocino
    José Tomás Tocino almost 4 years
    The link to the decorator is dead
  • DragonKnight
    DragonKnight over 3 years
    how to set Access-Control-Max-Age for flask_cors?
  • Anime no Sekai
    Anime no Sekai over 3 years
    1) Headers are from HTTP connections, not HTML documents 2) You can just open up your devtools on any modern browser to display the headers of the connection under the 'Network' tab without installing any extension 3) You might need to use * in Access-Control-Allow-Origin if you don't want to to set a custom header for every request and you want for example to create a Public API
  • mattyb
    mattyb about 3 years
    Worked for me!! Thank you. This should be the accepted answer!
  • mattyb
    mattyb about 3 years
    See the one-liner below as well: stackoverflow.com/a/46637194/3559330
  • oeter
    oeter almost 3 years
    @Matteo It's been 4 years, but you can do something like this: response = make_response(render_template('index.html', foo=42)), response.headers.add('Access-Control-Allow-Origin', '*')
  • Matteo
    Matteo almost 3 years
    @oeter - Thanks for the tip!
  • Alaa M.
    Alaa M. almost 3 years
    Adding my 2 cents... I still got a CORS error, but when I ran heroku logs --tail I saw a module import error. Then I added Flask-Cors==3.0.10 to the requirements.txt file, and it worked. I did not have to restart the dynos. Also you can run pip freeze > requirements.txt to get all the module requirements automatically.
  • Noname
    Noname about 2 years
    doesn't seem to work with POST requests
  • jeff
    jeff about 2 years
    Please note that the wildcard should be avoided, i.e. setting Access-Control-Allow-Origin to '*'. As per Mozilla docs: Access-Control-Allow-Origin specifies either a single origin which tells browsers to allow that origin to access the resource; or else — for requests without credentials — the " * " wildcard tells browsers to allow any origin to access the resource. You wouldn't want any origin from accessing your endpoints.