Come abilitare CORS in flask


92

Sto cercando di effettuare una richiesta di origine incrociata utilizzando jquery ma continua a essere rifiutata con il messaggio

XMLHttpRequest non può caricare http: // ... Nessuna intestazione "Access-Control-Allow-Origin" è presente sulla risorsa richiesta. Origine ... non è quindi consentito l'accesso.

Sto usando flask, heroku e jquery

il codice client ha questo aspetto:

$(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);
            }
        });
    }); 
});

sul lato heroku sto usando la fiaschetta ed è così

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

Risposte:


174

Ecco cosa ha funzionato per me quando mi sono schierato su Heroku.

http://flask-cors.readthedocs.org/en/latest/
Installa flask-cors eseguendo - 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!"

37
Più 1 per Hello Cross Origin World!
Simon Nicholls,

era l'unica soluzione che funziona per me. Grazie!
psc37

2
Sei un salvatore di vita! Ha funzionato come un fascino.
Rohit Swami

Ciao! Puoi aiutarmi a capire cosa succede nel mio caso? Ho scritto una semplice API usando Python / Flask, senza nemmeno una vista. L'ho raggiunto tramite curlcomandi. Ora ho scritto una breve pagina html e ho provato a fare una richiesta con il metodo JS fetch () alla mia API che basata su Heroku e ho l'errore CORS. Dopo aver applicato il tuo codice, il mio terminale ha iniziato a rispondermi con il codice HTML (HTTP / 1.1 503 Service non disponibile) invece del JSON. Quale potrebbe essere un errore qui? Grazie!!
Nikita Basharkin

5
Prima che qualcuno copi questo codice nella sua applicazione, controlla la documentazione perché sono necessarie solo alcune di queste righe.
rovyko

46

OK, non penso che lo snippet ufficiale menzionato da galuszkak debba essere usato ovunque, dovremmo preoccuparci del caso che durante il gestore possa essere attivato qualche bug come la hello_worldfunzione. Indipendentemente dal fatto che la risposta sia corretta o errata, l' Access-Control-Allow-Originintestazione è ciò che dobbiamo preoccuparci. Quindi, la cosa è molto semplice, proprio come il muggito:

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

Questo è tutto ~~


Questo mi ha anche aiutato per un piccolo progetto con operazioni CRUD di base. Non c'è bisogno di niente di stravagante, basta bypassare l'errore :)
Narshe

34

Ho appena affrontato lo stesso problema e sono arrivato a credere che le altre risposte siano un po 'più complicate di quanto dovrebbero essere, quindi ecco il mio approccio per coloro che non vogliono fare affidamento su più biblioteche o decoratori:

Una richiesta CORS consiste in realtà di due richieste HTTP. Una richiesta di verifica preliminare e quindi una richiesta effettiva che viene effettuata solo se la verifica preliminare ha esito positivo.

La richiesta di verifica preliminare

Prima dell'effettiva POSTrichiesta interdominio , il browser emetterà una OPTIONSrichiesta. Questa risposta non dovrebbe restituire alcun corpo, ma solo alcune intestazioni rassicuranti che dicono al browser che va bene eseguire questa richiesta interdominio e non fa parte di qualche attacco di scripting cross site.

Ho scritto una funzione Python per costruire questa risposta usando la make_responsefunzione dal flaskmodulo.

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

Questa risposta è un carattere jolly che funziona per tutte le richieste. Se vuoi la sicurezza aggiuntiva ottenuta da CORS, devi fornire una whitelist di origini, intestazioni e metodi.

Questa risposta convincerà il tuo browser (Chrome) ad andare avanti e ad eseguire la richiesta effettiva.

La richiesta effettiva

Quando si serve la richiesta effettiva, è necessario aggiungere un'intestazione CORS, altrimenti il ​​browser non restituirà la risposta al codice JavaScript di richiamo. Invece la richiesta fallirà sul lato client. Esempio con jsonify

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

Ho anche scritto una funzione per questo.

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

permettendoti di restituire una battuta.

Codice finale

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

Grazie @Niels B. così tanto, mi hai risparmiato tempo. Ho già aggiunto la configurazione cors ma non l'ho impostata correttamente.
Günay Gültekin

1
Questa è di gran lunga la migliore risposta su questo problema CORS su Flask. Ha funzionato come un fascino! Grazie @Niels
Chandra Kanth

Grazie per la tua spiegazione molto dettagliata !! Questo è stato molto utile!
jones-chris

Usa molte soluzioni, tra cui CORS e la tua, ma non tutte funzionano per aws (segui questo esempio-- aws.amazon.com/getting-started/projects/… ), qualcuno sa cosa sta succedendo?
StereoMatching

Questa soluzione è davvero semplice ma elegante! Grazie, mi hai davvero risparmiato tempo.
Gerry

22

Se si desidera attivare CORS per tutte le rotte, quindi basta installare flask_cors estensione ( pip3 install -U flask_cors) e involucro appin questo modo: CORS(app).

Questo è sufficiente per farlo (l'ho testato con una POSTrichiesta di caricare un'immagine e ha funzionato per me):

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

Nota importante: se c'è un errore nel tuo percorso, diciamo che provi a stampare una variabile che non esiste, otterrai un messaggio relativo all'errore CORS che, di fatto, non ha nulla a che fare con CORS.


1
Molte grazie! Questa soluzione semplice e generale mi ha permesso di chiamare la mia API dal mio codice web React senza più il blocco CORS.
Sebastian Diaz

2
Grazie ! La parte importante delle note mi ha fatto risparmiare un bel po 'di tempo.
Gabriel

4

Prova i seguenti decoratori:

@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()

Questo decoratore verrebbe creato come segue:

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

Puoi anche controllare questo pacchetto Flask-CORS


continua a non funzionare. L'ho già provato e ho anche usato il pacchetto Flask-CORS. Penso che Flask-CORS sia costruito su questo
Lopes

3

Migliorare la soluzione descritta qui: https://stackoverflow.com/a/52875875/10299604

Con after_requestpossiamo gestire le intestazioni di risposta CORS evitando di aggiungere codice extra ai nostri endpoint:

    ### 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

2

La mia soluzione è un wrapper attorno a 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():
    ...

2

Tutte le risposte precedenti funzionano bene, ma probabilmente riceverai comunque un errore CORS, se l'applicazione genera un errore che non stai gestendo, come un errore di chiave, se non stai eseguendo correttamente la convalida dell'input, ad esempio. È possibile aggiungere un gestore degli errori per rilevare tutte le istanze di eccezioni e aggiungere intestazioni di risposta CORS nella risposta del server

Quindi definisci un gestore degli errori - 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

quindi usando la risposta di 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 

2

Ho risolto lo stesso problema in Python usando flask e con questa libreria. flask_cors

Riferimento: https://flask-cors.readthedocs.io/en/latest/


Sebbene questo collegamento possa rispondere alla domanda, è meglio includere le parti essenziali della risposta qui e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. - Dalla recensione
Jason Aller

0

Se non riesci a trovare il tuo problema e il tuo codice dovrebbe funzionare, è possibile che la tua richiesta stia raggiungendo il tempo massimo consentito da heroku per effettuare una richiesta. Heroku annulla le richieste se impiega più di 30 secondi.

Riferimento: https://devcenter.heroku.com/articles/request-timeout

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.