Como habilitar CORS em frasco

92

Estou tentando fazer uma solicitação de origem cruzada usando jquery, mas continua sendo rejeitada com a mensagem

XMLHttpRequest não pode carregar http: // ... Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Origem ... portanto, não é permitido o acesso.

Estou usando flask, heroku e jquery

o código do cliente é parecido com este:

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

do lado do heroku estou usando o frasco e é assim

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
fonte

Respostas:

174

Aqui está o que funcionou para mim quando implantei no Heroku.

http://flask-cors.readthedocs.org/en/latest/
Instale flask-cors executando - 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
fonte
37
Mais 1 para oi, mundo de origem cruzada!
Simon Nicholls,
foi a única solução que funcionou para mim. Obrigado!
psc37
2
Você é um salvador de vidas! Funcionou como um encanto.
Rohit Swami
Oi! Você poderia me ajudar a descobrir o que acontece no meu caso? Eu escrevi uma API simples usando Python / Flask, sem nem mesmo uma visão para ela. Eu alcancei por curlcomandos. Agora eu escrevi uma página html curta e tentando fazer uma solicitação com o método JS fetch () para minha API que baseado em Heroku e eu tenho o erro CORS. Depois de aplicar seu código, meu terminal começou a responder para mim com o código HTML (HTTP / 1.1 503 Serviço indisponível) em vez do JSON. O que pode ser um erro aqui? Obrigado!!
Nikita Basharkin
5
Antes que alguém copie este código em seu aplicativo, verifique a documentação porque apenas algumas dessas linhas são necessárias.
rovyko,
46

OK, não acho que o trecho oficial mencionado por galuszkak deva ser usado em todos os lugares, devemos nos preocupar com o caso de que algum bug possa ser acionado durante o manipulador, como a hello_worldfunção. Quer a resposta esteja correta ou incorreta, o Access-Control-Allow-Origincabeçalho é o que devemos nos preocupar. Então, a coisa é muito simples, assim como a seguir:

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

Isso é tudo ~~

Zhangqy
fonte
Isso também me ajudou em um pequeno projeto com operações CRUD básicas. Não precisa de nada sofisticado, apenas ignore o erro :)
Narshe
34

Acabei de enfrentar o mesmo problema e passei a acreditar que as outras respostas são um pouco mais complicadas do que precisam ser, então aqui está minha abordagem para aqueles que não querem depender de mais bibliotecas ou decoradores:

Na verdade, uma solicitação CORS consiste em duas solicitações HTTP. Uma solicitação de comprovação e, em seguida, uma solicitação real que só é feita se a comprovação for aprovada com êxito.

O pedido de comprovação

Antes da POSTsolicitação real de vários domínios , o navegador emitirá uma OPTIONSsolicitação. Essa resposta não deve retornar nenhum corpo, mas apenas alguns cabeçalhos tranquilizadores informando ao navegador que não há problema em fazer essa solicitação entre domínios e não faz parte de nenhum ataque de script entre sites.

Escrevi uma função Python para construir essa resposta usando a make_responsefunção do flaskmódulo.

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

Essa resposta é um caractere curinga que funciona para todas as solicitações. Se você deseja a segurança adicional obtida pelo CORS, deve fornecer uma lista de permissões de origens, cabeçalhos e métodos.

Esta resposta convencerá seu navegador (Chrome) a prosseguir e fazer a solicitação real.

O pedido real

Ao atender a solicitação real, você deve adicionar um cabeçalho CORS - caso contrário, o navegador não retornará a resposta ao código JavaScript de chamada. Em vez disso, a solicitação falhará no lado do cliente. Exemplo com jsonify

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

Eu também escrevi uma função para isso.

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

permitindo que você retorne um one-liner.

Código final

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.
fonte
Muito obrigado @Niels B., você economizou meu tempo. Eu adicionei a configuração do cors antes, mas não configurei corretamente.
Günay Gültekin
1
Esta é de longe a melhor resposta para este problema do CORS no Flask. Funcionou como um encanto! Obrigado @Niels
Chandra Kanth
Obrigado por sua explicação muito detalhada !! Isso foi muito útil!
jones-chris
Use muitas soluções, incluindo CORS e a sua, mas todas elas não funcionam para aws (siga este exemplo - aws.amazon.com/getting-started/projects/… ), alguém sabe o que está acontecendo?
StereoMatching
Esta solução é realmente simples, mas elegante! Obrigado, você realmente salvou meu tempo.
Gerry
22

Se você deseja ativar o CORS em todas as rotas, em seguida, basta instalar flask_cors extensão ( pip3 install -U flask_cors) e enrole appcomo este: CORS(app).

Isso é o suficiente para fazer isso (eu testei isso com uma POSTsolicitação para fazer upload de uma imagem e funcionou para mim):

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

Nota importante: se houver um erro em sua rota, digamos que você tente imprimir uma variável que não existe, você receberá uma mensagem relacionada ao erro CORS que, na verdade, não tem nada a ver com CORS.

Billal Begueradj
fonte
1
Muito obrigado! Essa solução simples e geral me permitiu chamar minha API a partir do meu código da web React sem mais o bloco CORS.
Sebastian Diaz
2
Obrigado ! A parte importante da nota me economizou muito tempo.
Gabriel
4

Experimente os seguintes decoradores:

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

Este decorador seria criado da seguinte maneira:

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

Você também pode verificar este pacote Flask-CORS

Newtt
fonte
Ainda não funciona. Já tentei isso e também usei o pacote Flask-CORS. Acho que o Flask-CORS foi construído em cima disso
Lopes
3

Melhorando a solução descrita aqui: https://stackoverflow.com/a/52875875/10299604

Com after_request, podemos lidar com os cabeçalhos de resposta CORS evitando adicionar código extra aos nossos endpoints:

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

Minha solução é um wrapper em torno de 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
fonte
2

Todas as respostas acima funcionam bem, mas você provavelmente ainda obterá um erro CORS, se o aplicativo lançar um erro que você não está tratando, como um erro de chave, se você não estiver fazendo a validação de entrada corretamente, por exemplo. Você pode adicionar um manipulador de erros para capturar todas as instâncias de exceções e adicionar cabeçalhos de resposta CORS na resposta do servidor

Portanto, defina um gerenciador de erros - 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

então usando a resposta de 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 
Edrich
fonte
2

Resolvi esse mesmo problema em python usando flask e com essa biblioteca. flask_cors

Referência: https://flask-cors.readthedocs.io/en/latest/

pedro Orozco
fonte
Embora este link possa responder à pergunta, é melhor incluir as partes essenciais da resposta aqui e fornecer o link para referência. As respostas somente com link podem se tornar inválidas se a página vinculada mudar. - Da avaliação
Jason Aller
0

Se você não consegue encontrar seu problema e seu código deve funcionar, pode ser que sua solicitação esteja atingindo o máximo de tempo que o heroku permite que você faça uma solicitação. O Heroku cancela solicitações se demorar mais de 30 segundos.

Referência: https://devcenter.heroku.com/articles/request-timeout

Até
fonte