Как включить CORS во флаконе

89

Я пытаюсь сделать запрос с перекрестным происхождением с помощью jquery, но он продолжает отклоняться с сообщением

XMLHttpRequest не может загрузить http: // ... На запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin». Origin ... поэтому не имеет доступа.

Я использую flask, heroku и jquery

код клиента выглядит так:

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

на стороне героку я использую колбу, и это примерно так

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
источник

Ответы:

165

Вот что у меня сработало, когда я развернулся на Heroku.

http://flask-cors.readthedocs.org/en/latest/
Установите flask-cors, запустив - 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!"
Дэниел Расмусон
источник
31
Плюс 1 за привет, мир кросс-происхождения!
Саймон Николс
это было единственное решение, которое мне подходит. Благодарность!
psc37,
1
Ты спаситель жизни! Работал как шарм.
Рохит Свами
Здравствуй! Не могли бы вы помочь мне разобраться, что происходит в моем случае? Я написал простой API, используя Python / Flask, даже не имея для этого представления. Я дошел до него curlкомандами. Теперь я написал короткую html-страницу и пытаюсь сделать запрос с помощью метода JS fetch () к моему API, который основан на Heroku, и у меня есть ошибка CORS. После того, как я применил ваш код, мой терминал начал отвечать мне кодом HTML (HTTP / 1.1 503 Service Unavailable) вместо JSON. В чем тут может быть ошибка? Спасибо!!
Никита Башаркин
5
Прежде чем кто-либо скопирует этот код в свое приложение, ознакомьтесь с документацией, потому что необходимы только некоторые из этих строк.
Ровыко
45

Хорошо, я не думаю, что официальный фрагмент, упомянутый galuszkak, следует использовать повсюду, мы должны учитывать случай, когда во время обработки может быть вызвана какая-то ошибка, такая как hello_worldфункция. Независимо от того, верен ли ответ или нет, Access-Control-Allow-Originмы должны беспокоиться о заголовке. Итак, все очень просто, как показано ниже:

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

Это все ~~

Чжанки
источник
Это также помогло мне в небольшом проекте с основными операциями CRUD. Не нужно ничего особенного, просто
Нарше
34

Я только что столкнулся с той же проблемой, и я пришел к выводу, что другие ответы немного сложнее, чем они должны быть, поэтому вот мой подход для тех, кто не хочет полагаться на дополнительные библиотеки или декораторы:

Запрос CORS фактически состоит из двух HTTP-запросов. Предварительный запрос, а затем фактический запрос, который выполняется только в том случае, если предварительная проверка проходит успешно.

Предполетный запрос

Перед фактическим междоменным POSTзапросом браузер выдаст OPTIONSзапрос. Этот ответ не должен возвращать никакого тела, а только некоторые обнадеживающие заголовки, сообщающие браузеру, что это нормально выполнять этот междоменный запрос и что он не является частью какой-либо атаки межсайтового скриптинга.

Я написал функцию Python для создания этого ответа, используя make_responseфункцию из flaskмодуля.

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

Этот ответ с подстановочными знаками работает для всех запросов. Если вам нужна дополнительная безопасность, обеспечиваемая CORS, вы должны предоставить белый список источников, заголовков и методов.

Этот ответ убедит ваш браузер (Chrome) выполнить фактический запрос.

Фактический запрос

При обслуживании фактического запроса вы должны добавить один заголовок CORS - иначе браузер не вернет ответ на вызывающий код JavaScript. Вместо этого запрос не будет выполнен на стороне клиента. Пример с jsonify

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

Я также написал для этого функцию.

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

позволяя вам вернуть однострочный.

Окончательный код

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., вы сэкономили мне время. Я уже добавлял конфигурацию cors раньше, но неправильно ее настроил.
Günay Gültekin
1
Это, безусловно, лучший ответ на эту проблему CORS во Flask. Работал как шарм! Спасибо @Niels
Chandra Kanth
Спасибо за очень подробное объяснение !! Это было очень полезно!
jones-chris
Используйте множество решений, включая CORS и ваше, но все они не работают для aws (следуйте этому примеру - aws.amazon.com/getting-started/projects/… ), кто-нибудь знает, что происходит?
StereoMatch
Это действительно простое, но элегантное решение! Спасибо, вы действительно сэкономили мне время.
Джерри
20

Если вы хотите включить CORS для всех маршрутов, а затем просто установить flask_cors расширение ( pip3 install -U flask_cors) и обертку , appкак это: CORS(app).

Для этого достаточно (я тестировал это с POSTзапросом на загрузку изображения, у меня это сработало):

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

Важное примечание: если в вашем маршруте есть ошибка, допустим, вы пытаетесь распечатать несуществующую переменную, вы получите сообщение об ошибке CORS, которое на самом деле не имеет ничего общего с CORS.

Биллал Бегерадж
источник
1
Большое спасибо! Это простое и общее решение позволило мне больше вызывать свой API из моего веб-кода React без блока CORS.
Себастьян Диас,
1
Спасибо ! Важная часть для заметок сэкономила мне довольно много времени.
Габриэль
4

Попробуйте следующие декораторы:

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

Этот декоратор будет создан следующим образом:

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

Вы также можете ознакомиться с этим пакетом Flask-CORS

Newtt
источник
все еще не работает. Я уже пробовал это, а также использовал пакет Flask-CORS. Я думаю, что Flask-CORS построен на этом
Лопес
2

Мое решение - оболочка вокруг 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

Улучшение решения, описанного здесь: https://stackoverflow.com/a/52875875/10299604

С помощью after_requestмы можем обрабатывать заголовки ответов CORS, избегая добавления дополнительного кода в наши конечные точки:

    ### 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
Фрэнк Эскобар
источник
0

Все приведенные выше ответы работают нормально, но вы все равно, вероятно, получите ошибку CORS, если приложение выдает ошибку, которую вы не обрабатываете, например, ошибку ключа, если вы, например, неправильно выполняете проверку ввода. Вы можете добавить обработчик ошибок, чтобы перехватить все экземпляры исключений и добавить заголовки ответа CORS в ответ сервера.

Итак, определите обработчик ошибок - 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

затем используя ответ Биллала :

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 
Эдрич
источник
0

Если вы не можете найти свою проблему и ваш код должен работать, возможно, ваш запрос просто достигает максимального времени, которое heroku позволяет вам сделать. Heroku отменяет запросы, если это занимает более 30 секунд.

Ссылка: https://devcenter.heroku.com/articles/request-timeout

До
источник
0

Я решил эту же проблему в python, используя колбу и эту библиотеку. flask_cors

Ссылка: https://flask-cors.readthedocs.io/en/latest/

Педро Ороско
источник
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - Из отзыва
Джейсон Аллер,