【问题标题】:Javascript - No 'Access-Control-Allow-Origin' header is present on the requested resourceJavascript - 请求的资源上不存在“Access-Control-Allow-Origin”标头
【发布时间】:2014-04-06 13:04:26
【问题描述】:

我需要通过 XmlHttpRequest 从 JavaScript 向 Python 服务器发送数据。因为我使用的是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”标头 资源。因此不允许访问 Origin 'null'。

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

【问题讨论】:

    标签: javascript python ajax flask cors


    【解决方案1】:

    我使用了 flask-cors 扩展。

    使用pip install flask-cors安装

    那就简单了

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

    这将允许所有域

    【讨论】:

    • 更新:flask.ext.cors 已弃用,请改用flask_cors
    • 这是 Flask 的最佳解决方案。
    • 这会导致安全问题吗?
    • 非常简洁的解决方案!它也对我有用! (点赞)
    • > 上面的解决方案对我不起作用你能提供一些指示/错误吗?此解决方案仍在我的许多 Flask API 堆栈中使用
    【解决方案2】:

    老问题,但对于未来遇到此问题的谷歌用户,我通过将以下内容添加到我的 app.py 文件中,为我的烧瓶-restful 应用解决了它(以及与 CORS 相关的其他一些下游问题):

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

    【讨论】:

    • 工作得很好。但我知道这将提供对应用程序中所有端点的访问,而不是在使用装饰器时仅提供几个端点。
    • 完美。这个解决方案很简单
    • 谢谢,适用于常规响应,但不适用于 flask.send_file(),有什么解决办法吗?
    【解决方案3】:

    我使用 decorator 让 Javascript 与 Flask 一起工作,并将“OPTIONS”添加到我的可接受方法列表中。装饰器应该在你的路由装饰器下面使用,像这样:

    @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
    

    【讨论】:

    • "NameError: name 'crossdomain' is not defined"。我应该导入什么来修复它?
    • 您需要将链接中的装饰器复制并粘贴到您的 Flask 应用程序中。一旦在那里,将定义跨域并且一切都会正常工作。
    • Python 3.x 用户的重要提示:帖子中链接到的装饰器代码不适用于 Python 3.x。它给出了“NameError: name 'basestring' is not defined”错误,因为 basestring 在 Python 3.x 中不可用。如果您使用“list”而不是“basestring”来更改代码,它可以工作。
    • 可以在here找到相同的装饰器(或几乎相同,没有差异),具有更多文档和cmets。另外,为什么要添加 options 方法?
    • 链接已关闭。请发布您使用的装饰器。
    【解决方案4】:

    使用 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 站点上实际上有一个很棒的 sn-p 来修改服务器端的 Access-Control-Allow-Origin 标头。 http://flask.pocoo.org/snippets/56/

      您有一个简单的方法,即允许每个 * 域访问您的 URL,或在标题中指定您选择的 URL。

      来自MDN's article on CORS

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

      【讨论】:

      【解决方案6】:

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

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

      只需从 Zachary 提供的链接中复制代码并将其放入 .py 文件中

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

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

      【讨论】:

        【解决方案7】:

        Access-Control-Allow-Origin 必须由服务器发送,而不是由您发送。当您调用另一个域时,浏览器会检查此标头是否由服务器返回。如果不是,则呼叫失败。我不懂Python,所以我不知道如何让你的服务器发送这个头,或者即使你可以修改服务器。

        【讨论】:

          猜你喜欢
          • 2021-05-15
          • 2013-11-29
          • 2014-07-28
          • 2014-01-19
          • 2013-12-07
          相关资源
          最近更新 更多