So aktivieren Sie CORS im Kolben

89

Ich versuche, eine jetzübergreifende Anfrage mit jquery zu stellen, diese wird jedoch weiterhin mit der Nachricht abgelehnt

XMLHttpRequest kann http: // ... nicht laden. In der angeforderten Ressource ist kein Header 'Access-Control-Allow-Origin' vorhanden. Origin ... ist daher kein Zugriff gestattet.

Ich benutze Flasche, Heroku und JQuery

Der Client-Code sieht folgendermaßen aus:

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

Auf der Heroku-Seite benutze ich eine Flasche und es ist so

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
quelle

Antworten:

165

Folgendes hat bei meinem Einsatz bei Heroku funktioniert.

http://flask-cors.readthedocs.org/en/latest/
Installieren Sie flask-cors, indem Sie - ausführen. 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
quelle
31
Plus 1 für Hallo Cross Origin Welt!
Simon Nicholls
Es war die einzige Lösung, die für mich funktioniert. Vielen Dank!
psc37
1
Du bist ein Lebensretter! Lief wie am Schnürchen.
Rohit Swami
Hallo! Können Sie mir helfen, herauszufinden, was in meinem Fall passiert? Ich habe eine einfache API mit Python / Flask geschrieben, ohne auch nur eine Ansicht dafür. Ich habe es durch curlBefehle erreicht. Jetzt habe ich eine kurze HTML-Seite geschrieben und versucht, eine Anfrage mit der JS-Methode fetch () an meine API zu senden, die auf Heroku basiert, und ich habe den CORS-Fehler. Nachdem ich Ihren Code angewendet hatte, antwortete mein Terminal mit HTML-Code (HTTP / 1.1 503 Service nicht verfügbar) anstelle von JSON. Was könnte hier ein Fehler sein? Vielen Dank!!
Nikita Basharkin
5
Bevor jemand diesen Code in seine Anwendung kopiert, lesen Sie bitte die Dokumentation, da nur einige dieser Zeilen benötigt werden.
Rovyko
45

OK, ich denke nicht, dass das von galuszkak erwähnte offizielle Snippet überall verwendet werden sollte, wir sollten den Fall befürchten, dass während des Handlers ein Fehler ausgelöst wird, wie z. B. die hello_worldFunktion. Unabhängig davon, ob die Antwort korrekt oder nicht korrekt Access-Control-Allow-Originist, sollten wir uns um den Header kümmern. Die Sache ist also sehr einfach, genau wie unten:

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

Das ist alles ~~

zhangqy
quelle
Dies half mir auch bei einem kleinen Projekt mit grundlegenden CRUD-Operationen. Keine Notwendigkeit etwas
Besonderes
34

Ich habe mich gerade mit dem gleichen Problem konfrontiert und bin zu der Überzeugung gelangt, dass die anderen Antworten etwas komplizierter sind als nötig. Daher hier mein Ansatz für diejenigen, die sich nicht auf mehr Bibliotheken oder Dekorateure verlassen möchten:

Eine CORS-Anforderung besteht tatsächlich aus zwei HTTP-Anforderungen. Eine Preflight-Anfrage und dann eine tatsächliche Anfrage, die nur gestellt wird, wenn der Preflight erfolgreich bestanden wurde.

Die Preflight-Anfrage

Vor der eigentlichen domänenübergreifenden POSTAnfrage gibt der Browser eine OPTIONSAnfrage aus. Diese Antwort sollte keinen Text zurückgeben, sondern nur einige beruhigende Header, die dem Browser mitteilen, dass diese domänenübergreifende Anforderung in Ordnung ist und nicht Teil eines Cross-Site-Scripting-Angriffs ist.

Ich habe eine Python-Funktion geschrieben, um diese Antwort mithilfe der make_responseFunktion aus dem flaskModul zu erstellen .

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

Diese Antwort ist ein Platzhalter, der für alle Anforderungen funktioniert. Wenn Sie die zusätzliche Sicherheit von CORS nutzen möchten, müssen Sie eine Whitelist mit Ursprüngen, Headern und Methoden bereitstellen.

Diese Antwort wird Ihren (Chrome) Browser davon überzeugen, die eigentliche Anfrage zu stellen.

Die eigentliche Anfrage

Wenn Sie die eigentliche Anfrage bearbeiten, müssen Sie einen CORS-Header hinzufügen. Andernfalls gibt der Browser die Antwort auf den aufrufenden JavaScript-Code nicht zurück. Stattdessen schlägt die Anforderung auf der Clientseite fehl. Beispiel mit jsonify

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

Ich habe auch eine Funktion dafür geschrieben.

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

So können Sie einen Einzeiler zurückgeben.

Endgültiger Code

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.
quelle
Vielen Dank an @Niels B., Sie haben mir Zeit gespart. Ich habe die Cors-Konfiguration bereits hinzugefügt, aber nicht richtig eingerichtet.
Günay Gültekin
1
Dies ist bei weitem die beste Antwort auf dieses CORS-Problem bei Flask. Lief wie am Schnürchen! Vielen Dank @Niels
Chandra Kanth
Vielen Dank für Ihre sehr ausführliche Erklärung! Das war sehr hilfreich!
Jones-Chris
Verwenden Sie viele Lösungen, einschließlich CORS und Ihrer, aber alle funktionieren nicht für aws (folgen Sie diesem Beispiel - aws.amazon.com/getting-started/projects/… ). Weiß jemand, was los ist?
StereoMatching
Diese Lösung ist wirklich einfach und doch elegant! Danke, du hast mir wirklich Zeit gespart.
Gerry
20

Wenn Sie CORS für alle Routen aktivieren möchten, installieren Sie einfach die Erweiterung flask_cors ( pip3 install -U flask_cors) und wickeln Sie sie appwie folgt ein : CORS(app).

Das reicht aus (ich habe dies mit der POSTAufforderung getestet , ein Bild hochzuladen, und es hat bei mir funktioniert):

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

Wichtiger Hinweis: Wenn Ihre Route einen Fehler enthält, nehmen wir an, Sie versuchen, eine nicht vorhandene Variable zu drucken. Sie erhalten eine CORS-Fehlermeldung, die tatsächlich nichts mit CORS zu tun hat.

Billal Begueradj
quelle
1
Danke vielmals! Mit dieser einfachen und allgemeinen Lösung konnte ich meine API aus meinem React-Webcode ohne den CORS-Block mehr aufrufen.
Sebastian Diaz
1
Danke ! Der wichtige Notizenteil hat mir ziemlich viel Zeit gespart.
Gabriel
4

Probieren Sie die folgenden Dekorateure aus:

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

Dieser Dekorateur würde wie folgt erstellt:

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

Sie können sich auch dieses Paket Flask-CORS ansehen

Newtt
quelle
funktioniert immer noch nicht. Das habe ich bereits versucht und auch das Flask-CORS-Paket verwendet. Ich denke, Flask-CORS ist darauf aufgebaut
Lopes
2

Meine Lösung ist ein Wrapper um 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
quelle
2

Verbesserung der hier beschriebenen Lösung: https://stackoverflow.com/a/52875875/10299604

Mit können after_requestwir die CORS-Antwortheader verarbeiten und vermeiden, unseren Endpunkten zusätzlichen Code hinzuzufügen:

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

Alle oben genannten Antworten funktionieren einwandfrei, aber es wird wahrscheinlich immer noch ein CORS-Fehler angezeigt, wenn die Anwendung einen Fehler auslöst, den Sie nicht behandeln, wie z. B. einen Schlüsselfehler, wenn Sie beispielsweise die Eingabevalidierung nicht ordnungsgemäß durchführen. Sie können einen Fehlerhandler hinzufügen, um alle Instanzen von Ausnahmen abzufangen und der Serverantwort CORS-Antwortheader hinzuzufügen

Definieren Sie also einen Fehlerhandler - 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

dann mit Billals Antwort:

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
quelle
0

Wenn Sie Ihr Problem nicht finden können und Ihr Code funktionieren sollte, kann es sein, dass Ihre Anfrage gerade die maximale Zeit erreicht, die Heroku Ihnen erlaubt, eine Anfrage zu stellen. Heroku storniert Anfragen, wenn es länger als 30 Sekunden dauert.

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

Bis
quelle
0

Ich habe das gleiche Problem in Python mit flask und mit dieser Bibliothek gelöst. flask_cors

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

pedro Orozco
quelle
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert. - Aus dem Review
Jason Aller