wake-up-neo.com

RESTful domänenübergreifendes Problem mit den Methoden Angular: PUT, OPTIONS

Ich habe eine kleine schreibgeschützte REST - API mit Flask Restful entwickelt, die PUT-Anforderungen von einer Handvoll Clients akzeptiert, die möglicherweise IP-Adressen ändern können. Meine Kunden sind eingebettete Chromium-Clients, die ein AngularJS-Frontend ausführen. Sie authentifizieren sich mit meiner API mit einem einfachen magischen Schlüssel - dies ist für meinen sehr begrenzten Umfang ausreichend. 

Ich teste jetzt die Bereitstellung meiner API, und ich stelle fest, dass die Angular-Clients versuchen, eine OPTIONS-http-Methode an meinen Flask-Dienst zu senden. Meine API antwortet mittlerweile mit einer 404 (da ich noch keinen OPTIONS-Handler geschrieben habe, nur einen PUT-Handler). Es scheint, dass Angular beim Senden von domänenübergreifenden Anforderungen, die nicht POST oder GET sind, eine OPTIONS-Methode vor dem Flug an den Server sendet, um sicherzustellen, dass die domänenübergreifende Anforderung akzeptiert wird, bevor sie die tatsächliche Anforderung sendet. Ist das richtig?

Wie kann ich jedoch alle domänenübergreifenden PUT-Anforderungen an die Flask Restful-API zulassen? Ich habe bereits zuvor domänenübergreifende Dekoratoren mit einer (nicht-restful) Flask-Instanz verwendet, aber muss ich auch einen OPTIONS-Handler in meine API schreiben?

32
b.b.

Ich habe das Problem behoben, indem ich mein Flask-Backend umgeschrieben habe, um mit einem Access-Control-Allow-Origin-Header in meiner PUT-Antwort zu antworten. Darüber hinaus habe ich in meiner Flask-App einen OPTIONS-Handler erstellt, um die Optionsmethode zu beantworten, indem ich dem folgte, was ich im http-RFC gelesen habe. 

Die Rückgabe für die PUT-Methode sieht folgendermaßen aus:

return restful.request.form, 201, {'Access-Control-Allow-Origin': '*'} 

Mein OPTIONS-Methodenhandler sieht folgendermaßen aus:

def options (self):
    return {'Allow' : 'PUT' }, 200, \
    { 'Access-Control-Allow-Origin': '*', \
      'Access-Control-Allow-Methods' : 'PUT,GET' }

@tbicr hat recht: Flask beantwortet die Methode OPTIONS automatisch für Sie. In meinem Fall wurde jedoch mit dieser Antwort nicht der Access-Control-Allow-Origin-Header übertragen. Daher erhielt mein Browser eine Antwort von der API, die den Eindruck erweckte, dass domänenübergreifende Anforderungen nicht zulässig waren. Ich habe die Optionsanforderung in meinem Fall überlastet und den ACAO-Header hinzugefügt, und der Browser schien damit zufrieden zu sein, und folgte OPTIONS mit einem PUT, der ebenfalls funktionierte. 

18
b.b.

Mit dem Modul Flask-CORS können Sie domänenübergreifende Anforderungen ausführen, ohne Ihren Code zu ändern.

from flask.ext.cors import CORS

app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})

Aktualisieren

Da Eric vorgeschlagen wurde, ist das Modul flask.ext.cors nun veraltet. Sie sollten stattdessen den folgenden Code verwenden:

from flask_cors import CORS

app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
45
Mathieu Rodic

Sie können den after_request-Hook verwenden:

 @ app.after_request 
 def after_request (Antwort): 
 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') 
 Antwort zurückgeben 

In diesem Tutorial finden Sie weitere Informationen - http://tutsbucket.com/tutorials/building-a-blog-using-flask-and-angularjs-part-1/

19
John Kenn

Sie haben Recht, OPTIONS-Methode, die jedes Mal vor einer realen Anforderung im Browser aufgerufen wird. OPTIONS response haben Methoden und Header erlaubt. Flasche verarbeitet automatisch OPTIONS-Anforderungen.

Um Zugriff für domänenübergreifende Anforderungen zu erhalten, muss die API über den Header Access-Control-Allow-Origin verfügen. Sie kann bestimmte Domänen enthalten. Wenn Sie jedoch Anforderungen von beliebigen Domänen zulassen möchten, können Sie sie auf Access-Control-Allow-Origin: * setzen.

Um CORS für flask einzurichten, können Sie einen Erweiterungscode anzeigen oder diese Erweiterung verwenden: https://github.com/wcdolphin/flask-cors/blob/master/flask_cors.py .

Um CORS für flask-restful einzurichten, schauen Sie sich diese Pull-Anfragen an: https://github.com/twilio/flask-restful/pull/122 und https://github.com/twilio/flask-restful/pull/131 . Aber es sieht so aus, als würde flask-restful es noch nicht standardmäßig unterstützen.

4
tbicr

Nur ein Update zu diesem Kommentar. Flask CORS ist der richtige Weg, aber flask.ext.cors ist veraltet.

verwenden Sie: from flask_cors import CORS

3
Eric

Wie wäre es mit dieser Problemumgehung: 

from flask import Flask
from flask.ext import restful
from flask.ext.restful import Api
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config.from_object('config')

#flask-sqlalchemy
db = SQLAlchemy(app)

#flask-restful
api = restful.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')
  return response

import views

Ich habe dies aus diesem Tutorial entnommen. Funktioniert sehr gut. Ich denke, das ist der beste Ansatz, den ich bisher gesehen habe.

Die Rückgabe von {'Access-Control-Allow-Origin': '*'} an jedem Endpunkt scheint nicht effizient zu sein, da Sie ihn an jedem Endpunkt hinzufügen müssen. ein bisschen nervig ... zumindest für mich.

Ich habe @cors.crossdomain(Origin='*') ausprobiert, aber es scheint nur mit GET request zu funktionieren. 

2

Um Remote-CORS-Anforderungen auf Ihrer Web-Service-API zuzulassen, können Sie einfach Ihre restful-API für Flaschen wie folgt initialisieren:

from flask import Flask
from flask_restful import reqparse, abort, Api, Resource
from flask_cors import CORS

app = Flask(__name__)
cors = CORS(app, resources={r"*": {"origins": "*"}})
api = Api(app)

Dies fügt den CORS-Header Ihrer API-Instanz hinzu und ermöglicht eine CORS-Anforderung auf jedem Pfad aus jedem Origin.

0
domih

Ich benutze gerne eine Dekoration, um sie zu lösen.

def cross_Origin(origin="*"):
    def cross_Origin(func):
        @functools.wraps(func)
        def _decoration(*args, **kwargs):
            ret = func(*args, **kwargs)
            _cross_Origin_header = {"Access-Control-Allow-Origin": Origin,
                                    "Access-Control-Allow-Headers":
                                        "Origin, X-Requested-With, Content-Type, Accept"}
            if isinstance(ret, Tuple):
                if len(ret) == 2 and isinstance(ret[0], dict) and isinstance(ret[1], int):
                    # this is for handle response like: ```{'status': 1, "data":"ok"}, 200```
                    return ret[0], ret[1], _cross_Origin_header
                Elif isinstance(ret, basestring):
                    response = make_response(ret)
                    response.headers["Access-Control-Allow-Origin"] = Origin
                    response.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept"
                    return response
                Elif isinstance(ret, Response):
                    ret.headers["Access-Control-Allow-Origin"] = Origin
                    ret.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept"
                    return ret
                else:
                    raise ValueError("Cannot handle cross Origin, because the return value is not matched!")
            return ret

        return _decoration

    return cross_Origin

Und dann, verwenden Sie Dekoration in Ihrer erholsamen API.

class ExampleRestfulApi(Resource)
    @cross_Origin()
    def get(self):
        # allow all cross domain access
        pass

    @cross_Origin(origin="192.168.1.100")
    def post(self):
        # allow 192.168.1.100 access
        pass
0
gordonpro