Resuelva el intercambio de recursos de origen cruzado con Flask

81

Para la siguiente ajaxsolicitud de publicación para Flask( ¿cómo puedo usar los datos publicados desde ajax en el matraz? ):

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

Me sale un Cross Origin Resource Sharing (CORS)error:

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.

Intenté resolverlo de las dos formas siguientes, pero ninguna parece funcionar.

  1. Usando Flask-CORS

Esta es una Flaskextensión para el manejo CORSque debería hacer posible AJAX de origen cruzado.

Mi pythonServer.py usando esta solución:

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. Uso de un decorador de matraces específico

Este es un fragmento de código oficial de Flask que define un decorador que debe permitir CORSlas funciones que decora.

Mi pythonServer.py usando esta solución:

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

¿Puede dar alguna indicación de por qué es así?

Matteo
fuente
te diste cuenta? Me encuentro exactamente con el mismo problema :(
shao
Esta es una pregunta antigua, pero solo para estar seguro: ¿Reinició su servidor Flask? También me preguntaba por qué obtuve el mismo error, incluso si pensaba que todo era exactamente como debería. Resulta que tienes que reiniciar el servidor para que realmente surta efecto
Juha Untinen

Respuestas:

49

Funcionó como un campeón, después de una pequeña modificación en su código

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

Reemplacé * por localhost. Como leo en muchos blogs y publicaciones, debe permitir el acceso para un dominio específico

Satish
fuente
2
En caso de que alguien esté usando planos, debe agregar el CORS () a cada plano, por ejemplo: my_blueprint = Blueprint ('my_bp_name', name , url_prefix = "/ my-prefix") CORS (my_blueprint)
BLang
88

Puede obtener los resultados con un simple:

@app.route('your route', methods=['GET'])
def yourMethod(params):
    response = flask.jsonify({'some': 'data'})
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response
Salvador Dalí
fuente
1
¡Mejor que agregar una implementación de dominio cruzado!
Florescu Cătălin
Simple y efectivo
Anthony
4
¡Brillante! Estoy de acuerdo, simple y eficaz. En mi opinión se debe aceptar la respuesta
PALEN
3
@Salvador Dali: ¿Sabe cuál es la forma correcta de permitir el origen cruzado si estoy representando una plantilla en lugar de solo un objeto json? es decir, la última línea de código yourMethodes:return render_template('template.html',some_var = response)
Matteo
3
¿Podría usarse esto en un método de publicación? Intenté un método de carga de archivos y fallé.
W.Leto
60

Bueno, me enfrenté al mismo problema. Para nuevos usuarios que puedan aterrizar en esta página. Simplemente siga su documentación oficial.

Instalar flask-cors

pip install -U flask-cors

luego, después de la inicialización de la aplicación, inicialice flask-corscon los argumentos predeterminados:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
   return "Hello, cross-origin-world!"
Nagashayan
fuente
Esto me da el siguiente error El acceso a XMLHttpRequest en 'my_domain' desde el origen ' 127.0.0.1:5000 ' ha sido bloqueado por la política de CORS: La respuesta a la solicitud de verificación previa no pasa la verificación de control de acceso: El 'Access-Control-Allow- El encabezado Origin 'contiene varios valores' 127.0.0.1:5000 , * ', pero solo se permite uno.
humblePilgrim
No me funciona. stackoverflow.com/questions/59652099/…
WP McNeill
¡Funciona para mí también Tks!
Jose
¡Trabajó para mi! Muchas gracias :)
XpressGeek
cómo configurar Access-Control-Max-Agepara flask_cors?
DragonKnight
5

Bien podría hacer de esto una respuesta. Tuve el mismo problema hoy y no fue un problema más de lo esperado. Después de agregar la funcionalidad CORS, debe reiniciar su servidor Flask ( ctrl + c-> python manage.py runserver, o cualquier método que use)) para que el cambio surta efecto, incluso si el código es correcto. De lo contrario, CORS no funcionará en la instancia activa.

Así es como se ve para mí y funciona (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"
        })

En mi aplicación de React console.log:

Sending request:
GET /stuff
With parameters:
null
bundle.js:17316 Received data from Api:
{"stuff": "Here be stuff"}
Juha Untinen
fuente
4

Tenga en cuenta que configurar el Access-Control-Allow-Originencabezado en el objeto de respuesta Flask está bien en muchos casos (como este), pero no tiene ningún efecto cuando se sirven activos estáticos (en una configuración de producción, al menos). Eso es porque los activos estáticos son servidos directamente por el servidor web frontal (generalmente Nginx o Apache). Entonces, en ese caso, debe configurar el encabezado de respuesta en el nivel del servidor web, no en Flask.

Para obtener más detalles, consulte este artículo que escribí hace un tiempo, que explica cómo configurar los encabezados (en mi caso, estaba tratando de hacer un servicio entre dominios de los activos de Font Awesome).

Además, como dijo @Satu, es posible que deba permitir el acceso solo para un dominio específico, en el caso de las solicitudes JS AJAX. Para solicitar activos estáticos (como archivos de fuentes), creo que las reglas son menos estrictas y se acepta más el acceso a cualquier dominio.

Jaza
fuente
3

Solía decorador dada por Armin Ronacher con pequeñas modificaciones (debido a las diferentes cabeceras que son solicitados por el cliente) .Y que trabajó para mí. (donde uso angular como solicitante que solicita el tipo de aplicación / json).

El código se modifica ligeramente en los lugares siguientes,

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 enviará un tipo application / json, de lo contrario será text / html. los encabezados se agregan como el cliente en mi solicitud de caso para esos encabezados

 const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin':'*'
      })
    };
    return this.http.post<any>(url, item,httpOptions)
D.Waasala
fuente
2
El enlace al decorador está muerto
José Tomás Tocino
2

Nota: La ubicación de cross_origin debe ser correcta y las dependencias están instaladas. En el lado del cliente, asegúrese de especificar el tipo de servidor de datos que consume. Por ejemplo, application / json o text / html

Para mí, el código escrito a continuación hizo magia.

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
fuente
¿Por qué haces lo mismo (support_credentials = True) en cross_origin?
Eziz Durdyyev
0

Luché mucho con algo similar. Intente lo siguiente:

  1. Utilice algún tipo de complemento de navegador que pueda mostrar los encabezados HTML.
  2. Ingrese la URL de su servicio y vea los valores de encabezado devueltos.
  3. Asegúrese de que Access-Control-Allow-Origin esté configurado en uno y solo un dominio, que debería ser el origen de la solicitud. No establezca Access-Control-Allow-Origin en *.

Si esto no ayuda, eche un vistazo a este artículo. Está en PHP, pero describe exactamente qué encabezados deben establecerse en qué valores para que CORS funcione.

CORS que funciona en IE, Firefox, Chrome y Safari

Por Kristian
fuente
0

Puede que llegue tarde en esta pregunta, pero los pasos siguientes solucionaron el problema

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
Ramkumar Khubchandani
fuente