Comment activer CORS dans flask

89

J'essaie de faire une demande d'origine croisée en utilisant jquery mais elle continue d'être rejetée avec le message

XMLHttpRequest ne peut pas charger http: // ... Aucun en-tête 'Access-Control-Allow-Origin' n'est présent sur la ressource demandée. Origine ... n'est donc pas autorisé à accéder.

J'utilise flask, heroku et jquery

le code client ressemble à ceci:

$(document).ready(function() {
    $('#submit_contact').click(function(e){
        e.preventDefault();
        $.ajax({
            type: 'POST',
            url: 'http://...',
            // data: [
            //      { name: "name", value: $('name').val()},
            //      { name: "email", value: $('email').val() },
            //      { name: "phone", value: $('phone').val()},
            //      { name: "description", value: $('desc').val()}
            //
            // ],
            data:"name=3&email=3&phone=3&description=3",
            crossDomain:true,
            success: function(msg) {
                alert(msg);
            }
        });
    }); 
});

du côté heroku, j'utilise un flacon et c'est comme ça

from flask import Flask,request
from flask.ext.mandrill import Mandrill
try:
    from flask.ext.cors import CORS  # The typical way to import flask-cors
except ImportError:
    # Path hack allows examples to be run without installation.
    import os
    parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    os.sys.path.insert(0, parentdir)

    from flask.ext.cors import CORS
app = Flask(__name__)

app.config['MANDRILL_API_KEY'] = '...'
app.config['MANDRILL_DEFAULT_FROM']= '...'
app.config['QOLD_SUPPORT_EMAIL']='...'
app.config['CORS_HEADERS'] = 'Content-Type'

mandrill = Mandrill(app)
cors = CORS(app)

@app.route('/email/',methods=['POST'])
def hello_world():
    name=request.form['name']
    email=request.form['email']
    phone=request.form['phone']
    description=request.form['description']

    mandrill.send_email(
        from_email=email,
        from_name=name,
        to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
        text="Phone="+phone+"\n\n"+description
    )

    return '200 OK'

if __name__ == '__main__':
    app.run()
Lopes
la source

Réponses:

165

Voici ce qui a fonctionné pour moi lorsque j'ai déployé à Heroku.

http://flask-cors.readthedocs.org/en/latest/
Installez flask-cors en exécutant - pip install -U flask-cors

from flask import Flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'

@app.route("/")
@cross_origin()
def helloWorld():
  return "Hello, cross-origin-world!"
Daniel Rasmuson
la source
31
Plus 1 pour bonjour le monde d'origine croisée!
Simon Nicholls
c'était la seule solution qui fonctionne pour moi. Merci!
psc37 le
1
Vous êtes un sauveur de vie! A travaillé comme un charme.
Rohit Swami
Salut! Pouvez-vous m'aider à comprendre ce qui se passe dans mon cas? J'ai écrit une API simple en utilisant Python / Flask, sans même une vue pour cela. Je l'ai atteint par des curlcommandes. Maintenant, j'ai écrit une courte page html et j'essaie de faire une demande avec la méthode JS fetch () à mon API qui est basée sur Heroku et j'ai l'erreur CORS. Après avoir appliqué votre code, mon terminal a commencé à me répondre avec du code HTML (HTTP / 1.1 503 Service Unavailable) au lieu du JSON. Qu'est-ce qui pourrait être une erreur ici? Merci!!
Nikita Basharkin
5
Avant que quiconque copie ce code dans son application, veuillez consulter la documentation car seules certaines de ces lignes sont nécessaires.
rovyko le
45

OK, je ne pense pas que l'extrait officiel mentionné par galuszkak devrait être utilisé partout, nous devrions nous préoccuper du cas où un bogue pourrait être déclenché pendant le gestionnaire tel que hello_worldfunction. Que la réponse soit correcte ou incorrecte, l'en- Access-Control-Allow-Origintête est ce dont nous devons nous préoccuper. Donc, la chose est très simple, tout comme ci-dessous:

@blueprint.after_request # blueprint can also be app~~
def after_request(response):
    header = response.headers
    header['Access-Control-Allow-Origin'] = '*'
    return response

C'est tout ~~

Zhangqy
la source
Cela m'a également aidé pour un petit projet avec des opérations CRUD de base. Pas besoin de quelque chose d'extraordinaire, il suffit de contourner l'erreur :)
Narshe
34

Je viens de faire face au même problème et j'en suis venu à croire que les autres réponses sont un peu plus compliquées qu'elles ne devraient l'être, alors voici mon approche pour ceux qui ne veulent pas compter sur plus de bibliothèques ou de décorateurs:

Une requête CORS se compose en fait de deux requêtes HTTP. Une demande de contrôle en amont, puis une demande réelle qui n'est effectuée que si le contrôle en amont réussit.

La demande de contrôle en amont

Avant la POSTdemande réelle de plusieurs domaines , le navigateur émettra une OPTIONSdemande. Cette réponse ne doit renvoyer aucun corps, mais seulement quelques en-têtes rassurants indiquant au navigateur qu'il est correct de faire cette requête inter-domaines et que cela ne fait pas partie d'une attaque de script intersite.

J'ai écrit une fonction Python pour construire cette réponse en utilisant la make_responsefonction du flaskmodule.

def _build_cors_prelight_response():
    response = make_response()
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add("Access-Control-Allow-Headers", "*")
    response.headers.add("Access-Control-Allow-Methods", "*")
    return response

Cette réponse est un joker qui fonctionne pour toutes les demandes. Si vous souhaitez bénéficier de la sécurité supplémentaire obtenue par CORS, vous devez fournir une liste blanche des origines, des en-têtes et des méthodes.

Cette réponse convaincra votre navigateur (Chrome) d'aller de l'avant et de faire la demande réelle.

La demande réelle

Lors du traitement de la demande réelle, vous devez ajouter un en-tête CORS, sinon le navigateur ne retournera pas la réponse au code JavaScript appelant. Au lieu de cela, la demande échouera du côté client. Exemple avec jsonify

response = jsonify({"order_id": 123, "status": "shipped"}
response.headers.add("Access-Control-Allow-Origin", "*")
return response

J'ai aussi écrit une fonction pour ça.

def _corsify_actual_response(response):
    response.headers.add("Access-Control-Allow-Origin", "*")
    return response

vous permettant de retourner un one-liner.

Code final

from flask import Flask, request, jsonify, make_response
from models import OrderModel

flask_app = Flask(__name__)

@flask_app.route("/api/orders", methods=["POST", "OPTIONS"])
def api_create_order():
    if request.method == "OPTIONS": # CORS preflight
        return _build_cors_prelight_response()
    elif request.method == "POST": # The actual request following the preflight
        order = OrderModel.create(...) # Whatever.
        return _corsify_actual_response(jsonify(order.to_dict()))
    else
        raise RuntimeError("Weird - don't know how to handle method {}".format(request.method))

def _build_cors_prelight_response():
    response = make_response()
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add('Access-Control-Allow-Headers', "*")
    response.headers.add('Access-Control-Allow-Methods', "*")
    return response

def _corsify_actual_response(response):
    response.headers.add("Access-Control-Allow-Origin", "*")
    return response
Niels B.
la source
Merci beaucoup @Niels B., vous avez économisé mon temps. J'ai déjà ajouté la configuration des cors mais je ne l'ai pas correctement configurée.
Günay Gültekin
1
C'est de loin la meilleure réponse à ce problème CORS sur Flask. A travaillé comme un charme! Merci @Niels
Chandra Kanth
Merci pour votre explication très détaillée !! Cela a été très utile!
jones-chris
Utilisez de nombreuses solutions, y compris CORS et la vôtre, mais toutes ne fonctionnent pas pour aws (suivez cet exemple - aws.amazon.com/getting-started/projects/… ), est-ce que quelqu'un sait ce qui se passe?
StereoMatching
Cette solution est vraiment simple mais élégante! Merci, tu as vraiment gagné mon temps.
Gerry le
20

Si vous souhaitez activer CORS pour toutes les voies, puis il suffit d' installer flask_cors extension ( pip3 install -U flask_cors) et enveloppe appcomme ceci: CORS(app).

C'est suffisant pour le faire (j'ai testé cela avec une POSTdemande de téléchargement d'une image, et cela a fonctionné pour moi):

from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # This will enable CORS for all routes

Remarque importante: s'il y a une erreur dans votre itinéraire, disons que vous essayez d'imprimer une variable qui n'existe pas, vous obtiendrez un message d'erreur CORS qui, en fait, n'a rien à voir avec CORS.

Billal Begueradj
la source
1
Merci beaucoup! Cette solution simple et générale m'a permis d'appeler mon API à partir de mon code web React sans plus le bloc CORS.
Sebastian Diaz
1
Merci ! La partie note importante m'a fait gagner beaucoup de temps.
Gabriel il y a
4

Essayez les décorateurs suivants:

@app.route('/email/',methods=['POST', 'OPTIONS']) #Added 'Options'
@crossdomain(origin='*')                          #Added
def hello_world():
    name=request.form['name']
    email=request.form['email']
    phone=request.form['phone']
    description=request.form['description']

    mandrill.send_email(
        from_email=email,
        from_name=name,
        to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
        text="Phone="+phone+"\n\n"+description
    )

    return '200 OK'

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

Ce décorateur serait créé comme suit:

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):

    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

Vous pouvez également consulter ce package Flask-CORS

Newtt
la source
ne fonctionne toujours pas. J'ai déjà essayé cela et j'ai également utilisé le package Flask-CORS. Je pense que Flask-CORS est construit sur cela
Lopes
2

Ma solution est un wrapper autour de app.route:

def corsapp_route(path, origin=('127.0.0.1',), **options):
    """
    Flask app alias with cors
    :return:
    """

    def inner(func):
        def wrapper(*args, **kwargs):
            if request.method == 'OPTIONS':
                response = make_response()
                response.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
                response.headers.add('Access-Control-Allow-Headers', ', '.join(origin))
                response.headers.add('Access-Control-Allow-Methods', ', '.join(origin))
                return response
            else:
                result = func(*args, **kwargs)
            if 'Access-Control-Allow-Origin' not in result.headers:
                result.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
            return result

        wrapper.__name__ = func.__name__

        if 'methods' in options:
            if 'OPTIONS' in options['methods']:
                return app.route(path, **options)(wrapper)
            else:
                options['methods'].append('OPTIONS')
                return app.route(path, **options)(wrapper)

        return wrapper

    return inner

@corsapp_route('/', methods=['POST'], origin=['*'])
def hello_world():
    ...
yurzs
la source
2

Amélioration de la solution décrite ici: https://stackoverflow.com/a/52875875/10299604

Avec after_requestnous pouvons gérer les en-têtes de réponse CORS en évitant d'ajouter du code supplémentaire à nos points de terminaison:

    ### CORS section
    @app.after_request
    def after_request_func(response):
        origin = request.headers.get('Origin')
        if request.method == 'OPTIONS':
            response = make_response()
            response.headers.add('Access-Control-Allow-Credentials', 'true')
            response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
            response.headers.add('Access-Control-Allow-Headers', 'x-csrf-token')
            response.headers.add('Access-Control-Allow-Methods',
                                'GET, POST, OPTIONS, PUT, PATCH, DELETE')
            if origin:
                response.headers.add('Access-Control-Allow-Origin', origin)
        else:
            response.headers.add('Access-Control-Allow-Credentials', 'true')
            if origin:
                response.headers.add('Access-Control-Allow-Origin', origin)

        return response
    ### end CORS section
Frank Escobar
la source
0

Toutes les réponses ci-dessus fonctionnent bien, mais vous obtiendrez probablement toujours une erreur CORS, si l'application renvoie une erreur que vous ne gérez pas, comme une erreur de clé, si vous ne faites pas correctement la validation d'entrée, par exemple. Vous pouvez ajouter un gestionnaire d'erreurs pour intercepter toutes les instances d'exceptions et ajouter des en-têtes de réponse CORS dans la réponse du serveur

Définissez donc un gestionnaire d'erreurs - errors.py:

from flask import json, make_response, jsonify
from werkzeug.exceptions import HTTPException

# define an error handling function
def init_handler(app):

    # catch every type of exception
    @app.errorhandler(Exception)
    def handle_exception(e):

        #loggit()!          

        # return json response of error
        if isinstance(e, HTTPException):
            response = e.get_response()
            # replace the body with JSON
            response.data = json.dumps({
                "code": e.code,
                "name": e.name,
                "description": e.description,
            })
        else:
            # build response
            response = make_response(jsonify({"message": 'Something went wrong'}), 500)

        # add the CORS header
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.content_type = "application/json"
        return response

puis en utilisant la réponse de Billal :

from flask import Flask
from flask_cors import CORS

# import error handling file from where you have defined it
from . import errors

app = Flask(__name__)
CORS(app) # This will enable CORS for all routes
errors.init_handler(app) # initialise error handling 
Edrich
la source
0

Si vous ne trouvez pas votre problème et que votre code devrait fonctionner, il se peut que votre requête n'atteigne que le temps maximum que Heroku vous permet de faire une requête. Heroku annule les demandes si cela prend plus de 30 secondes.

Référence: https://devcenter.heroku.com/articles/request-timeout

Till
la source
0

J'ai résolu ce même problème en python en utilisant flask et avec cette bibliothèque. flask_cors

Référence: https://flask-cors.readthedocs.io/en/latest/

Pedro Orozco
la source
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses aux liens uniquement peuvent devenir invalides si la page liée change. - De l'avis
Jason Aller