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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:25:26  来源:igfitidea点击:

How to enable CORS in flask

jquerypythonherokuflaskcors

提问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

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