Javascript - 请求的资源上没有 'Access-Control-Allow-Origin' 标头

60

我需要通过JavaScript从Python服务器发送数据,使用XmlHttpRequest。因为我正在使用localhost,所以需要使用CORS。我使用了Flask框架及其模块flask_cors

这是我的JavaScript代码:

    var xmlhttp;
    if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp = new XMLHttpRequest();
    }
    else {// code for IE6, IE5
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.open("POST", "http://localhost:5000/signin", true);
    var params = "email=" + email + "&password=" + password;


    xmlhttp.onreadystatechange = function() {//Call a function when the state changes.
        if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            alert(xmlhttp.responseText);
        }
    }
    xmlhttp.send(params);

以及 Python 代码:

@app.route('/signin', methods=['POST'])
@cross_origin()
def sign_in():
    email = cgi.escape(request.values["email"])
    password = cgi.escape(request.values["password"])

但是当我执行它时,我收到了这个消息:

XMLHttpRequest无法加载localhost:5000/signin。请求的资源上没有“Access-Control-Allow-Origin”标头。因此,不允许访问来源为“null”的内容。

我该如何解决它?我知道我需要使用一些“Access-Control-Allow-Origin”标头,但我不知道如何在这段代码中实现它。顺便说一下,我需要使用纯JavaScript。

7个回答

97

我已经使用了flask-cors扩展。

使用pip install flask-cors进行安装。

然后就很简单了。

from flask_cors import CORS
app = Flask(__name__)
CORS(app)

这将允许所有域名


8
更新:flask.ext.cors 已经被弃用,请使用 flask_cors 替代! - Sterling Archer
1
这是 Flask 的最佳解决方案。 - Vahid Amiri
5
会导致安全问题吗? - Pitto
非常简洁的解决方案!对我也起作用了!(点赞) - Outcast
2
以上解决方案对我不起作用。你能提供一些指示/错误吗?这个解决方案仍然在我的一些Flask API堆栈中使用。 - campervancoder
显示剩余4条评论

61

虽然这是一个老问题,但为了未来遇到同样问题的谷歌搜索者,我解决了这个问题(以及一些涉及CORS的其他下游问题),方法是在我的flask-restful应用程序的app.py文件中添加以下内容:

app = Flask(__name__)
api = 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,OPTIONS')
  return response


if __name__ == '__main__':
    app.run()

1
工作得非常好。但是当使用装饰器时,这将提供对应用程序中所有端点的访问权限,而不仅仅是一些端点。 - prasunnair
1
完美。这个解决方案非常简单。 - xuanhai266
谢谢,对于常规响应有效,但对于flask.send_file()无效,有什么解决方法吗? - Daniel Braun

37

我使用这个装饰器并将"OPTIONS"添加到我的可接受方法列表中,使Javascript与Flask配合运行。该装饰器应该在你的路由装饰器下面使用,就像这样:

@app.route('/login', methods=['POST', 'OPTIONS'])
@crossdomain(origin='*')
def login()
    ...

编辑: 链接似乎已经失效。以下是我使用的装饰器。

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):
    """Decorator function that allows crossdomain requests.
      Courtesy of
      https://blog.skyred.fi/articles/better-crossdomain-snippet-for-flask.html
    """
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    # use str instead of basestring if using Python 3.x
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    # use str instead of basestring if using Python 3.x
    if not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        """ Determines which methods are allowed
        """
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        """The decorator function
        """
        def wrapped_function(*args, **kwargs):
            """Caries out the actual cross domain code
            """
            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)
            h['Access-Control-Allow-Credentials'] = 'true'
            h['Access-Control-Allow-Headers'] = \
                "Origin, X-Requested-With, Content-Type, Accept, Authorization"
            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

2
"NameError:名称“crossdomain”未定义". 我该导入什么来解决它?" - Jaroslav Klimčík
2
你需要将链接中的装饰器复制并粘贴到你的Flask应用程序中。一旦完成,crossdomain将被定义并且所有的功能都将正常工作。 - Zachary Jacobi
13
对于Python 3.x用户的重要说明:帖子中链接到的修饰符代码不适用于Python 3.x。它会出现“NameError: name 'basestring' is not defined”的错误,因为在Python 3.x中没有basestring。如果您将代码中的“basestring”更改为“list”,它就可以工作了。 - Zafer
1
相同的装饰器(或几乎相同,没有区别)可以在此处找到:http://flask.pocoo.org/snippets/56/,其中包含更多的文档和注释。另外,为什么要添加选项方法? - Ulysse BN
@UlysseBN 根据HTTP标准,当请求足够“复杂”(除了GET之外的任何请求),需要使用“OPTIONS”以符合CORS规范。 - Juha Untinen
显示剩余2条评论

23

使用 Python 2.7 版本时

app = Flask(__name__)
api = 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,OPTIONS')
  return response


if __name__ == '__main__':
    app.run()

在运行Python3或以上版本时,使用命令pip install flask-cors安装Flask-CORS。然后添加以下内容:

from flask_cors import CORS
app = Flask(__name__)
CORS(app)

5

Flask网站上实际上有一个很棒的代码片段,可以修改服务器端的Access-Control-Allow-Origin头。 http://flask.pocoo.org/snippets/56/

你可以选择简单的方法,即允许每个*域访问你的URL,或在头部指定你选择的URL。

MDN关于CORS的文章中得知:

在这种情况下,服务器响应Access-Control-Allow-Origin: *,这意味着该资源可以以跨站点的方式被任何域访问。 如果http://bar.other的资源所有者希望将对该资源的访问限制为仅来自http://foo.example,则应发送: Access-Control-Allow-Origin:http://foo.example


1
解决方案的链接已经失效。 - c4collins
在2020年,请尝试使用此插件:Flask-CORS - boxmein

2

我使用了 Zachary 的解决方案。效果很好。

对于那些想知道在哪里放置新装饰器的人:

只需复制 Zachary 提供的链接中的代码并将其放在一个 .py 文件中即可.

将它放在您的 python 模块所在的文件夹中 (根据您使用的系统和是否使用虚拟环境而有所不同)。

在 Flask 应用程序中,从新创建的 python 模块中导入方法 crossdomain 并使用它。


0

Access-Control-Allow-Origin 必须由服务器发送,而不是您发送。当您呼叫其他域时,浏览器会检查服务器是否返回此标头。如果没有返回,则呼叫失败。我不了解 Python,因此不知道如何让您的服务器发送此标头,甚至不确定您是否可以修改服务器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接