Estoy tratando de hacer una solicitud de origen cruzado usando jquery pero sigue siendo rechazada con el mensaje
XMLHttpRequest no puede cargar http: // ... No hay un encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado. Origen ... por tanto, no se permite el acceso.
Estoy usando flask, heroku y jquery.
el código del cliente se ve así:
$(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);
}
});
});
});
en el lado de heroku estoy usando un matraz y es así
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()
curl
órdenes. Ahora escribí una página html corta e intenté hacer una solicitud con el método JS fetch () a mi API que, según Heroku y tengo el error CORS. Después de aplicar su código, mi terminal comenzó a responderme con código HTML (HTTP / 1.1 503 Servicio no disponible) en lugar del JSON. ¿Qué podría ser un error aquí? ¡¡Gracias!!De acuerdo, no creo que el fragmento oficial mencionado por galuszkak deba usarse en todas partes, deberíamos preocuparnos por el caso de que se pueda desencadenar algún error durante el controlador, como la
hello_world
función. Ya sea que la respuesta sea correcta o incorrecta, elAccess-Control-Allow-Origin
encabezado es lo que debemos preocuparnos. Entonces, la cosa es muy simple, como a continuación:@blueprint.after_request # blueprint can also be app~~ def after_request(response): header = response.headers header['Access-Control-Allow-Origin'] = '*' return response
Eso es todo ~~
fuente
Acabo de enfrentar el mismo problema y llegué a creer que las otras respuestas son un poco más complicadas de lo necesario, así que este es mi enfoque para aquellos que no quieren depender de más bibliotecas o decoradores:
Una solicitud CORS en realidad consta de dos solicitudes HTTP. Una solicitud de verificación previa y luego una solicitud real que solo se realiza si la verificación previa pasa con éxito.
La solicitud de verificación previa
Antes de la
POST
solicitud real entre dominios , el navegador emitirá unaOPTIONS
solicitud. Esta respuesta no debe devolver ningún cuerpo, sino solo algunos encabezados tranquilizadores que le dicen al navegador que está bien hacer esta solicitud entre dominios y que no es parte de ningún ataque de scripts entre sitios.Escribí una función de Python para construir esta respuesta usando la
make_response
función delflask
mó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
Esta respuesta es un comodín que funciona para todas las solicitudes. Si desea la seguridad adicional obtenida por CORS, debe proporcionar una lista blanca de orígenes, encabezados y métodos.
Esta respuesta convencerá a su navegador (Chrome) de seguir adelante y realizar la solicitud real.
La solicitud real
Al atender la solicitud real, debe agregar un encabezado CORS; de lo contrario, el navegador no devolverá la respuesta al código JavaScript de invocación. En cambio, la solicitud fallará en el lado del cliente. Ejemplo con jsonify
response = jsonify({"order_id": 123, "status": "shipped"} response.headers.add("Access-Control-Allow-Origin", "*") return response
También escribí una función para eso.
def _corsify_actual_response(response): response.headers.add("Access-Control-Allow-Origin", "*") return response
permitiéndole devolver un 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
fuente
Si desea habilitar CORS para todas las rutas, a continuación, sólo tiene que instalar flask_cors extensión (
pip3 install -U flask_cors
) y la envoltura deapp
la siguiente manera:CORS(app)
.Eso es suficiente para hacerlo (probé esto con una
POST
solicitud para cargar una imagen, y funcionó para mí):from flask import Flask from flask_cors import CORS app = Flask(__name__) CORS(app) # This will enable CORS for all routes
Nota importante: si hay un error en su ruta, digamos que intenta imprimir una variable que no existe, obtendrá un mensaje relacionado con el error CORS que, de hecho, no tiene nada que ver con CORS.
fuente
Prueba los siguientes 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 se crearía de la siguiente manera:
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
También puede consultar este paquete Flask-CORS
fuente
Mi solución es una envoltura alrededor 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(): ...
fuente
Mejorando la solución descrita aquí: https://stackoverflow.com/a/52875875/10299604
Con
after_request
podemos manejar los encabezados de respuesta CORS evitando agregar código extra a nuestros 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
fuente
Todas las respuestas anteriores funcionan bien, pero probablemente obtendrá un error CORS si la aplicación arroja un error que no está manejando, como un error de clave, si no está haciendo la validación de entrada correctamente, por ejemplo. Puede agregar un controlador de errores para detectar todas las instancias de excepciones y agregar encabezados de respuesta CORS en la respuesta del servidor
Así que defina un controlador de errores: 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
luego usando la respuesta 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
fuente
Si no puede encontrar su problema y su código debería funcionar, es posible que su solicitud esté llegando al máximo de tiempo que heroku le permite hacer una solicitud. Heroku cancela las solicitudes si demora más de 30 segundos.
Referencia: https://devcenter.heroku.com/articles/request-timeout
fuente
Resolví este mismo problema en Python usando un matraz y con esta biblioteca. flask_cors
Referencia: https://flask-cors.readthedocs.io/en/latest/
fuente