2014-03-04 19 views
24

我需要通過XmlHttpRequest從JavaScript發送數據到Python服務器。因爲我使用本地主機,所以我需要使用CORS。我正在使用Flask框架和他的模塊flask_cors。如JavaScript我有這樣的: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 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。謝謝

回答

24

我通過使用此decorator,讓Javascript與Flask一起工作,並將「OPTIONS」添加到我可接受的方法列表中。該裝飾應路線裝飾下使用,如:

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

編輯: 鏈接似乎被打破。這是我使用的裝飾器。

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)) 
    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(): 
     """ 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:name'crossdomain'is not defined」。我應該導入什麼來修復它? –

+2

您需要將鏈接中的裝飾器複製並粘貼到Flask應用程序中。一旦在那裏,crossdomain將被定義,一切都將工作。 –

+4

Python 3.x用戶的重要注意事項:在帖子中鏈接到的裝飾器代碼對Python 3.x不起作用。它給出「NameError:name」basestring'未定義「錯誤,因爲basestring在Python 3.x中不可用。如果你通過使用「list」而不是「basestring」來改變代碼,它就可以工作。 – Zafer

0

訪問控制允許來源必須由服務器發送,而不是由您。當你打電話給另一個域時,瀏覽器檢查這個頭是否被服務器返回。如果不是,則呼叫失敗。我不知道Python,所以我不知道如何讓你的服務器發送這個頭文件,或者即使你可以修改服務器。

6

實際上,有一個輝煌的片段在現場燒瓶修改Access-Control-Allow-Origin頭的服務器端。 http://flask.pocoo.org/snippets/56/

您可以輕鬆地從那裏獲得允許每個*域訪問您的URL或指定您在標題內選擇的URL的簡單方法。

MDN's article on 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 .

38

我已經使用了燒瓶-CORS擴展。

安裝使用pip install flask-cors

然後它只是

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

這將允許所有域

+4

更新:'flask.ext.cors'已棄用,請現在使用'flask_cors'! –

+1

這是Flask的最佳解決方案。 – VSG24

+0

這會導致安全問題嗎? – Pitto

2

我用從扎卡里的解決方案。效果很好。

對於那些誰不知道在何處放置新的裝飾:

只需將代碼從扎卡里提供的鏈接複製和

將它放置在一個.py文件的文件夾在你的Python模塊(根據您使用的系統以及您是否使用虛擬環境而有所不同)。

在燒瓶應用程序中,從新創建的python模塊導入方法crossdomain並使用它。

21

老問題,但這個問題未來的Google,我解決了它(並具有CORS做一些其他下游問題)我燒瓶的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() 
+3

爲我完美工作,謝謝! – eddiewould

+0

工作得很好。但我得到這個將提供訪問應用程序中的所有端點,而不僅僅是使用裝飾器時的幾個端點。 – prasunnair

+0

完美。這個解決方案非常簡單 – xuanhai266

6

當使用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運行或提前, 安裝燒瓶-CORS使用命令pip install flask-cors 的添加如下:

from flask_cors import CORS 
app = Flask(__name__) 
CORS(app) 
相關問題