如何启用烧瓶中的 CORS

我正在尝试使用 jquery 发出一个跨源请求,但它一直被消息拒绝

XMLHttpRequest 无法加载 http://... 没有“ Access-Control-allow-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);
}
});
});
});

在 Heroku 这边,我用的是烧瓶,它是这样的

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()
272702 次浏览

试试下面的装潢师:

@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

您还可以查看这个包 烧瓶-CORS

以下是我在赫罗库部署时的成功经验。

Http://flask-cors.readthedocs.org/en/latest/
通过运行-安装烧瓶 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!"

好的,我不认为 galuszkak 提到的官方代码片段应该到处使用,我们应该考虑在处理程序(如 hello_world函数)中可能触发一些 bug 的情况。无论响应是正确的还是不正确的,我们应该关心的是 Access-Control-Allow-Origin报头。因此,它非常简单,就像下面的片段:

# define your bluprint
from flask import Blueprint
blueprint = Blueprint('blueprint', __name__)


# put this sippet ahead of all your bluprints
# blueprint can also be app~~
@blueprint.after_request
def after_request(response):
header = response.headers
header['Access-Control-Allow-Origin'] = '*'
# Other headers can be added here if needed
return response


# write your own blueprints with business logics
@blueprint.route('/test', methods=['GET'])
def test():
return "test success"

就是这样

我刚刚遇到了同样的问题,我开始相信其他的答案比它们需要的要复杂一些,所以这里是我为那些不想依赖于更多的图书馆或装修师的人提供的方法:

CORS 请求实际上由两个 HTTP 请求组成。一个飞行前请求,然后是一个实际的请求,只有在飞行前成功通过才会发出。

飞行前的请求

在实际的跨域 POST请求之前,浏览器将发出 OPTIONS请求。这个响应不应该返回任何主体,而只是一些令人放心的标题,告诉浏览器这个跨域请求是正常的,它不是某种跨网站脚本攻击的一部分。

我编写了一个 Python 函数来使用 flask模块中的 make_response函数构建这个响应。

def _build_cors_preflight_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_preflight_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_preflight_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

如果您想为所有路线启用 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 无关。

我的解决方案是围绕 app.path 的一个包装器:

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

上面的所有响应都可以正常工作,但是如果应用程序抛出一个您没有处理的错误,例如键错误,如果您没有正确地进行输入验证,您仍然可能会得到 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

如果您无法找到您的问题,并且您的代码应该可以工作,那么可能是您的请求刚刚达到 heroku 允许您发出请求的最大时间。如果超过30秒,Heroku 会取消请求。

参考资料: https://devcenter.heroku.com/articles/request-timeout

我在 python 中使用 flask 和这个库解决了同样的问题。 烧瓶 在文件 Init.py 中:

#pip install flask_cors


from flask_cors import CORS


app = Flask(__name__)
CORS(app)
cors = CORS(app, resource={
r"/*":{
"origins":"*"
}
})

就是这样。

参考资料: https://flask-cors.readthedocs.io/en/latest/

改进这里描述的解决方案: 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