Rozwiąż współdzielenie zasobów między źródłami za pomocą Flask

87

Dla następującego ajaxżądania posta Flask( jak mogę użyć danych wysłanych z ajax w kolbie? ):

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

Wyskakuje mi Cross Origin Resource Sharing (CORS)błąd:

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.

Próbowałem rozwiązać to na dwa następujące sposoby, ale żaden z nich nie działa.

  1. Korzystanie z Flask-CORS

Jest to Flaskrozszerzenie obsługi, CORSktóre powinno umożliwić obsługę AJAX między źródłami.

Mój pythonServer.py korzystający z tego rozwiązania:

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. Korzystanie z określonego dekoratora kolb

To jest oficjalny fragment kodu Flask definiujący dekorator, który powinien zezwalać CORSna funkcje, które ozdabia.

Mój pythonServer.py korzystający z tego rozwiązania:

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

Czy możesz wskazać, dlaczego tak jest?

Matteo
źródło
zrozumiałeś? Natrafiam na dokładnie ten sam problem :(
shao,
To stare pytanie, ale dla pewności: czy zrestartowałeś serwer Flask? Zastanawiałem się również, dlaczego mam ten sam błąd, nawet jeśli wszystko jest dokładnie tak, jak powinno. Okazuje się, że musisz zrestartować serwer, aby naprawdę
zaczął

Odpowiedzi:

53

Działał jak mistrz, po niewielkiej modyfikacji kodu

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

Zastąpiłem * przez localhost. Ponieważ czytałem w wielu blogach i postach, powinieneś zezwolić na dostęp dla określonej domeny

Satish
źródło
2
Jeśli ktoś używa schematów, musisz dodać CORS () do każdego planu, na przykład: my_blueprint = Blueprint ('my_bp_name', name , url_prefix = "/ my-prefix") CORS (my_blueprint)
BLang
91

Możesz uzyskać wyniki za pomocą prostego:

@app.route('your route', methods=['GET'])
def yourMethod(params):
    response = flask.jsonify({'some': 'data'})
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response
Salvador Dali
źródło
1
Lepiej niż dodanie implementacji crossdomain!
Florescu Cătălin
1
Prosty i skuteczny
Anthony
5
Znakomity! Zgadzam się, proste i skuteczne. IMO Powinna zostać zaakceptowana odpowiedź
PALEN
3
@Salvador Dali - Czy wiesz, jaki jest właściwy sposób zezwalania na cross origin, jeśli renderuję szablon zamiast tylko obiektu json? tj. ostatnia linia kodu w yourMethodto:return render_template('template.html',some_var = response)
Matteo
3
Czy można to wykorzystać w metodzie postu? Próbowałem metody przesyłania plików i nie udało mi się.
W.Leto,
64

Cóż, miałem ten sam problem. Dla nowych użytkowników, którzy mogą wylądować na tej stronie. Wystarczy postępować zgodnie z ich oficjalną dokumentacją.

Zainstalować korki do kolb

pip install -U flask-cors

następnie po zainicjowaniu aplikacji zainicjuj flask-corsz domyślnymi argumentami:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
   return "Hello, cross-origin-world!"
Nagashayan
źródło
Daje mi to następujący błąd. Dostęp do XMLHttpRequest pod adresem „moja_domena” ze źródła „ 127.0.0.1:5000 ” został zablokowany przez zasady CORS: Odpowiedź na żądanie inspekcji wstępnej nie przechodzi kontroli dostępu: „Access-Control-Allow- Nagłówek Origin ”zawiera wiele wartości„ 127.0.0.1:5000 , * ”, ale tylko jedna jest dozwolona.
humblePilgrim
Nie działa na mnie. stackoverflow.com/questions/59652099/…
WP McNeill
U mnie też działa Tks!
Jose
Pracował dla mnie!
Wielkie
jak ustawić Access-Control-Max-Ageza flask_cors?
DragonKnight,
5

Równie dobrze może to być odpowiedź. Miałem dzisiaj ten sam problem i nie był to problem bardziej niż oczekiwano. Po dodaniu funkcji CORS należy zrestartować serwer Flask ( ctrl + c-> python manage.py runserverlub jakąkolwiek metodę, której używasz)), aby zmiana odniosła skutek, nawet jeśli kod jest poprawny. W przeciwnym razie CORS nie będzie działać w aktywnym wystąpieniu.

Oto jak to wygląda u mnie i działa (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"
        })

W mojej aplikacji console.log w React:

Sending request:
GET /stuff
With parameters:
null
bundle.js:17316 Received data from Api:
{"stuff": "Here be stuff"}
Juha Untinen
źródło
4

Zwróć uwagę, że ustawienie Access-Control-Allow-Originnagłówka w obiekcie odpowiedzi Flask jest w wielu przypadkach poprawne (na przykład w tym), ale nie ma wpływu na obsługę zasobów statycznych (przynajmniej w konfiguracji produkcyjnej). Dzieje się tak, ponieważ statyczne zasoby są obsługiwane bezpośrednio przez przedni serwer sieciowy (zwykle Nginx lub Apache). Więc w takim przypadku musisz ustawić nagłówek odpowiedzi na poziomie serwera WWW, a nie w Flasku.

Aby uzyskać więcej informacji, zobacz ten artykuł, który napisałem jakiś czas temu, wyjaśniając, jak ustawić nagłówki (w moim przypadku próbowałem udostępniać zasoby Font Awesome w wielu domenach).

Ponadto, jak powiedział @Satu, może być konieczne zezwolenie na dostęp tylko dla określonej domeny, w przypadku żądań JS AJAX. Jeśli chodzi o żądanie zasobów statycznych (takich jak pliki czcionek), myślę, że reguły są mniej surowe, a zezwalanie na dostęp dla dowolnej domeny jest bardziej akceptowane.

Jaza
źródło
3

Użyłem dekoratora podanego przez Armina Ronachera z niewielkimi modyfikacjami (ze względu na różne nagłówki, o które prosi klient) i to zadziałało. (gdzie używam angular jako requester żądający typu application / json).

Kod jest nieznacznie zmodyfikowany w poniższych miejscach,

from flask import jsonify

@app.route('/my_service', methods=['POST', 'GET','OPTIONS'])
@crossdomain(origin='*',headers=['access-control-allow-origin','Content-Type'])
def my_service():
    return jsonify(foo='cross domain ftw')

jsonify wyśle ​​typ aplikacji / json, w przeciwnym razie będzie to tekst / html. nagłówki są dodawane jako żądanie klienta w moim przypadku dla tych nagłówków

 const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin':'*'
      })
    };
    return this.http.post<any>(url, item,httpOptions)
D.Waasala
źródło
2
Link do dekoratora nie działa
José Tomás Tocino
2

Uwaga: Umieszczenie cross_origin powinno być poprawne, a zależności są zainstalowane. Po stronie klienta upewnij się, że określono rodzaj zużywanego przez serwer danych. Na przykład application / json lub text / html

U mnie poniższy kod zrobił magię

from flask import Flask,request,jsonify
from flask_cors import CORS,cross_origin
app=Flask(__name__)
CORS(app, support_credentials=True)
@app.route('/api/test', methods=['POST', 'GET','OPTIONS'])
@cross_origin(supports_credentials=True)
def index():
    if(request.method=='POST'):
     some_json=request.get_json()
     return jsonify({"key":some_json})
    else:
        return jsonify({"GET":"GET"})


if __name__=="__main__":
    app.run(host='0.0.0.0', port=5000)
Dila Gurung
źródło
dlaczego robisz to samo (support_credentials = True) w cross_origin?
Eziz Durdyyev
1

Mogę się spóźnić z tym pytaniem, ale poniższe kroki rozwiązały problem

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
Ramkumar Khubchandani
źródło
0

Dużo walczyłem z czymś podobnym. Spróbuj wykonać następujące czynności:

  1. Użyj jakiejś wtyczki do przeglądarki, która może wyświetlać nagłówki HTML.
  2. Wprowadź adres URL swojej usługi i wyświetl zwrócone wartości nagłówka.
  3. Upewnij się, że Access-Control-Allow-Origin jest ustawiona na jedną i tylko jedną domenę, która powinna być źródłem żądania. Nie ustawiaj Access-Control-Allow-Origin na *.

Jeśli to nie pomoże, zajrzyj do tego artykułu. Jest w PHP, ale opisuje dokładnie, które nagłówki muszą być ustawione na jakie wartości, aby CORS działał.

CORS, który działa w IE, Firefox, Chrome i Safari

Per Kristian
źródło