Python 如何在烧瓶中启用 CORS
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25594893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to enable CORS in flask
提问by Lopes
I am trying to make a cross origin request using jquery but it keeps being reject with the message
我正在尝试使用 jquery 发出跨源请求,但它一直被消息拒绝
XMLHttpRequest cannot load http://... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ... is therefore not allowed access.
XMLHttpRequest 无法加载 http://... 请求的资源上不存在“Access-Control-Allow-Origin”标头。Origin ... 因此不允许访问。
I am using flask, heroku, and jquery
我正在使用烧瓶、heroku 和 jquery
the client code looks like this:
客户端代码如下所示:
$(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);
}
});
});
});
on the heroku side i am using flask and it is like this
在 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()
回答by Newtt
Try the following decorators:
尝试以下装饰器:
@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()
This decorator would be created as follows:
该装饰器将按如下方式创建:
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
You can also check out this package Flask-CORS
你也可以看看这个包Flask-CORS
回答by Daniel Rasmuson
Here is what worked for me when I deployed to Heroku.
当我部署到 Heroku 时,这对我有用。
http://flask-cors.readthedocs.org/en/latest/
Install flask-cors by running -
pip install -U flask-cors
http://flask-cors.readthedocs.org/en/latest/
通过运行安装flask-cors -
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!"
回答by zhangqy
OK, I don't think the official snippet mentioned by galuszkak should be used everywhere, we should concern the case that some bug may be triggered during the handler such as hello_worldfunction. Whether the response is correct or uncorrect, the Access-Control-Allow-Originheader is what we should concern. So, thing is very simple, just like bellow:
好吧,我认为galuszkak提到的官方代码片段不应该到处使用,我们应该关注处理过程中可能会触发一些bug的情况,例如hello_worldfunction。无论响应正确与否,Access-Control-Allow-Originheader才是我们应该关心的。所以,事情很简单,就像下面这样:
@blueprint.after_request # blueprint can also be app~~
def after_request(response):
header = response.headers
header['Access-Control-Allow-Origin'] = '*'
return response
That is all~~
就这些~~
回答by Niels B.
I've just faced the same issue and I came to believe that the other answers are a bit more complicated than they need to be, so here's my approach for those who don't want to rely on more libraries or decorators:
我刚刚遇到了同样的问题,我开始相信其他答案比他们需要的要复杂一些,所以对于那些不想依赖更多库或装饰器的人,这是我的方法:
A CORS request actually consists of two HTTP requests. A preflight request and then an actual request that is only made if the preflight passes successfully.
一个 CORS 请求实际上由两个 HTTP 请求组成。预检请求,然后是仅在预检成功通过时才发出的实际请求。
The preflight request
预检请求
Before the actual cross domain POSTrequest, the browser will issue an OPTIONSrequest. This response should not return any body, but only some reassuring headers telling the browser that it's alright to do this cross-domain request and it's not part of some cross site scripting attack.
在实际跨域POST请求之前,浏览器会发出OPTIONS请求。此响应不应返回任何正文,而应仅返回一些令人放心的标头,告诉浏览器可以执行此跨域请求,并且它不是某些跨站点脚本攻击的一部分。
I wrote a Python function to build this response using the make_responsefunction from the flaskmodule.
我编写了一个 Python 函数来使用模块中的make_response函数构建此响应flask。
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
This response is a wildcard one that works for all requests. If you want the additional security gained by CORS, you have to provide a whitelist of origins, headers and methods.
此响应是适用于所有请求的通配符。如果您希望通过 CORS 获得额外的安全性,则必须提供来源、标头和方法的白名单。
This response will convince your (Chrome) browser to go ahead and do the actual request.
此响应将说服您的 (Chrome) 浏览器继续执行实际请求。
The actual request
实际要求
When serving the actual request you have to add one CORS header - otherwise the browser won't return the response to the invoking JavaScript code. Instead the request will fail on the client-side. Example with jsonify
在处理实际请求时,您必须添加一个 CORS 标头 - 否则浏览器不会将响应返回给调用 JavaScript 代码。相反,请求将在客户端失败。jsonify 示例
response = jsonify({"order_id": 123, "status": "shipped"}
response.headers.add("Access-Control-Allow-Origin", "*")
return response
I also wrote a function for that.
我还为此编写了一个函数。
def _corsify_actual_response(response):
response.headers.add("Access-Control-Allow-Origin", "*")
return response
allowing you to return a one-liner.
允许您退回单线。
Final code
最终代码
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
回答by Billal Begueradj
If you want to enable CORS for all routes, then just install flask_corsextension (pip3 install -U flask_cors) and wrap applike this: CORS(app).
如果要启用CORS为所有路由,则只需安装flask_cors扩展(pip3 install -U flask_cors)和包 app这样的:CORS(app)。
That is enough to do it (I tested this with a POSTrequest to upload an image, and it worked for me):
这足以做到这一点(我POST通过上传图像的请求对此进行了测试,它对我有用):
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # This will enable CORS for all routes
Important note: if there is an error in your route, let us say you try to print a variable that does not exist, you will get a CORS error related message which, in fact, has nothing to do with CORS.
重要提示:如果您的路由出现错误,假设您尝试打印一个不存在的变量,您将收到一条与 CORS 错误相关的消息,该消息实际上与 CORS 无关。
回答by yurzs
My solution is a wrapper around app.route:
我的解决方案是围绕 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():
...
回答by Edrich
All the responses above work okay, but you'll still probably get a CORS error, if the application throws an error you are not handling, like a key-error, if you aren't doing input validation properly, for example. You could add an error handler to catch all instances of exceptions and add CORS response headers in the server response
上面的所有响应都可以正常工作,但是您仍然可能会收到 CORS 错误,例如,如果应用程序抛出您没有处理的错误,例如键错误,如果您没有正确进行输入验证,例如。您可以添加一个错误处理程序来捕获所有异常实例并在服务器响应中添加 CORS 响应标头
So define an error handler - errors.py:
所以定义一个错误处理程序——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
then using Billal'sanswer:
然后使用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
回答by Till
If you can't find your problem and you're code should work, it may be that your request is just reaching the maximum of time heroku allows you to make a request. Heroku cancels requests if it takes more than 30 seconds.
如果您找不到问题并且您的代码应该可以工作,则可能是您的请求刚刚达到 heroku 允许您发出请求的最长时间。如果超过 30 秒,Heroku 会取消请求。
Reference: https://devcenter.heroku.com/articles/request-timeout

