Frasco RESTful problema de dominio cruzado con Angular: métodos PUT, OPCIONES

He desarrollado una pequeña API REST de solo escritura con Flask Restful que acepta la solicitud PUT de un puñado de clientes que pueden tener direcciones IP cambiantes. Mis clientes son clientes Chromium incrustados que ejecutan un front-end AngularJS; se autentican con mi API con una simple tecla mágica, es suficiente para mi escala muy limitada.

Estoy probando la implementación de mi API ahora y me doy cuenta de que los clientes de Angular están intentando enviar métodos http de OPCIONES a mi servicio Flask. Mientras tanto, mi API está respondiendo con un 404 (ya que todavía no escribí un controlador OPTIONS, solo un controlador PUT). Parece que cuando se envían solicitudes de dominio cruzado que no son POST o GET, Angular enviará un método de OPCIONES previas al vuelo en el servidor para asegurarse de que la solicitud de dominio cruzado se acepte antes de enviar la solicitud real. ¿Está bien?

De todos modos, ¿cómo permito que todas las solicitudes PUT entre dominios a Flask Restful API? He usado decoradores de dominios cruzados con una instancia de Flask (no reparadora) antes, pero ¿tengo que escribir también un controlador de OPCIONES en mi API?

Con el módulo Flask-CORS , puede hacer solicitudes de dominios cruzados sin cambiar su código .

from flask.ext.cors import CORS app = Flask(__name__) cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) 

Actualizar

Como Eric sugirió, el módulo flask.ext.cors ahora está en desuso, deberías usar el siguiente código:

 from flask_cors import CORS app = Flask(__name__) cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) 

Puedes usar el gancho after_request:

 @ app.after_request
 def after_request (respuesta):
     response.headers.add ('Access-Control-Allow-Origin', '*')
     response.headers.add ('Access-Control-Allow-Headers', 'Content-Type, Authorization')
     response.headers.add ('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE')
     respuesta de retorno

Consulte este tutorial para obtener más detalles: http://tutsbucket.com/tutorials/building-a-blog-using-flask-and-angularjs-part-1/

Resolví el problema al volver a escribir mi backend Flask para responder con un encabezado de Access-Control-Allow-Origin en mi respuesta PUT. Además, creé un controlador de OPCIONES en mi aplicación Flask para responder al método de las opciones siguiendo lo que leí en el http RFC.

El retorno del método PUT se ve así:

 return restful.request.form, 201, {'Access-Control-Allow-Origin': '*'} 

El manejador de mi método OPTIONS se ve así:

 def options (self): return {'Allow' : 'PUT' }, 200, \ { 'Access-Control-Allow-Origin': '*', \ 'Access-Control-Allow-Methods' : 'PUT,GET' } 

@tbicr tiene razón: Flask SÍ responde automáticamente el método de OPCIONES. Sin embargo, en mi caso no transmitía el encabezado de Access-Control-Allow-Origin con esa respuesta, por lo que mi navegador estaba recibiendo una respuesta de la API que parecía implicar que no se permitían las solicitudes de varios dominios. Sobrecargué la solicitud de opciones en mi caso y agregué el encabezado ACAO, y el navegador parecía estar satisfecho con eso, y seguí las OPCIONES con un PUT que también funcionó.

Tienes razón, el método OPTIONS se llama cada vez antes de una solicitud real en el navegador. OPTIONS respuesta han permitido métodos y encabezados. Frasco automáticamente procesa las solicitudes de OPTIONS .

Para obtener acceso para la solicitud de varios dominios, la API debe tener el encabezado Access-Control-Allow-Origin . Puede contener dominios específicos, pero si desea permitir solicitudes de cualquier dominio, puede establecerlo en Access-Control-Allow-Origin: * .

Para configurar CORS para el flask , puede mirar un código de extensión o intentar usar esta extensión: https://github.com/wcdolphin/flask-cors/blob/master/flask_cors.py .

Para configurar CORS para que el flask-restful aspecto flask-restful consulte estas solicitudes de extracción: https://github.com/twilio/flask-restful/pull/122 y https://github.com/twilio/flask-restful/pull/131 . Pero parece que flask-restful todavía no lo admite de forma predeterminada.

Solo una actualización de este comentario. Flask CORS es el camino a seguir, pero flask.ext.cors está en desuso.

uso: from flask_cors import CORS

¿Qué tal esta solución?

 from flask import Flask from flask.ext import restful from flask.ext.restful import Api from flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__) app.config.from_object('config') #flask-sqlalchemy db = SQLAlchemy(app) #flask-restful api = restful.Api(app) @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Origin', '*') response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE') return response import views 

Tomé esto de este tutorial. Funciona muy bien En realidad, creo que este es el mejor enfoque que he visto hasta ahora.

La devolución de {'Access-Control-Allow-Origin': '*'} en cada punto final, no parece ser eficiente, ya que se debe agregar en cada punto final. un poco molesto …, al menos para mi.

Intenté @cors.crossdomain(origin='*') pero parece que solo funciona con la solicitud GET .

Me gusta usar una decoración para resolver.

 def cross_origin(origin="*"): def cross_origin(func): @functools.wraps(func) def _decoration(*args, **kwargs): ret = func(*args, **kwargs) _cross_origin_header = {"Access-Control-Allow-Origin": origin, "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept"} if isinstance(ret, tuple): if len(ret) == 2 and isinstance(ret[0], dict) and isinstance(ret[1], int): # this is for handle response like: ```{'status': 1, "data":"ok"}, 200``` return ret[0], ret[1], _cross_origin_header elif isinstance(ret, basestring): response = make_response(ret) response.headers["Access-Control-Allow-Origin"] = origin response.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept" return response elif isinstance(ret, Response): ret.headers["Access-Control-Allow-Origin"] = origin ret.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept" return ret else: raise ValueError("Cannot handle cross origin, because the return value is not matched!") return ret return _decoration return cross_origin 

Y luego, usa la decoración en tu api tranquilo.

 class ExampleRestfulApi(Resource) @cross_origin() def get(self): # allow all cross domain access pass @cross_origin(origin="192.168.1.100") def post(self): # allow 192.168.1.100 access pass 

Para permitir solicitudes de CORS remotas en su api de servicio web, simplemente puede inicializar su API de descanso del flask de la siguiente manera:

 from flask import Flask from flask_restful import reqparse, abort, Api, Resource from flask_cors import CORS app = Flask(__name__) cors = CORS(app, resources={r"*": {"origins": "*"}}) api = Api(app) 

Esto agrega el encabezado CORS a su instancia de api y permite una solicitud CORS en cada ruta desde cada origen.