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

60 投票
7 回答
102798 浏览
提问于 2025-04-17 20:50

我需要通过 XmlHttpRequest 从 JavaScript 向 Python 服务器发送数据。因为我是在本地开发,所以需要使用 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 个回答

5

其实在Flask网站上有一个很棒的代码片段,可以用来在服务器端修改Access-Control-Allow-Origin这个头信息。你可以在这里找到它:http://flask.pocoo.org/snippets/56/

你可以很简单地选择让所有的*域名都能访问你的网址,或者在头信息里指定你想允许的那些网址。

根据MDN关于CORS的文章

在这种情况下,服务器会返回Access-Control-Allow-Origin: *,这意味着这个资源可以被任何域名以跨站的方式访问。如果资源的拥有者在http://bar.other想要限制只有来自http://foo.example的访问,他们会返回:Access-Control-Allow-Origin: http://foo.example

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

如果你在使用 Python 3 或更高版本, 可以通过命令 pip install flask-cors 来安装 flask-cors。 然后添加以下内容:

from flask_cors import CORS
app = Flask(__name__)
CORS(app)
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()
98

我使用了flask-cors这个扩展。

可以通过命令 pip install flask-cors 来安装它。

然后只需要这样写:

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

这样就可以允许所有的域名访问了。

37

我通过使用这个装饰器,让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

撰写回答