关于python:Javascript – 请求的资源上没有’Access-Control-Allow-Origin’标头

Javascript - No 'Access-Control-Allow-Origin' header is present on the requested resource

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

作为JavaScript我有这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    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代码:

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

但是当我执行它时,我得到了这样的信息:

XMLHttpRequest cannot load localhost:5000/signin. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access.

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


我使用了烧瓶扩展。

使用pip install flask-cors安装

那就简单了

1
2
3
from flask_cors import CORS
app = Flask(__name__)
CORS(app)

这将允许所有域


老问题,但是对于有这个问题的未来googlers,我通过将以下内容添加到我的app.py文件中来解决它(以及与CORS有关的一些其他下游问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
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()


我使用这个装饰器让Javascript与Flask一起工作,并在我可接受的方法列表中添加"OPTIONS"。应该在路径装饰器下面使用装饰器,如下所示:

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

编辑:
链接似乎被打破了。这是我用的装饰器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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


使用python 2.7时

1
2
3
4
5
6
7
8
9
10
11
12
13
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
添加以下内容:

1
2
3
from flask_cors import CORS
app = Flask(__name__)
CORS(app)

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

您可以轻松地从那里开始,即允许每个*域访问您的URL,或指定您在标题内选择的URL。

来自MDN关于CORS的文章:

In this case, the server responds with a Access-Control-Allow-Origin: * which means that the
resource can be accessed by any domain in a cross-site manner. If the resource owners at
http://bar.other wished to restrict access to the resource to be only from
http://foo.example, they would send back:
Access-Control-Allow-Origin: http://foo.example.


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

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

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

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

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


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