0

我下面這個谷歌火力地堡教程:https://cloud.google.com/appengine/docs/standard/python/authenticating-users-firebase-appengine跨源請求阻止 - 創建實體GAE數據存儲

我在最後一部分地方增加了注到數據存儲,但是當我按下按鈕來添加筆記它不會做任何事情,並給我在Firefox Web控制檯中的以下錯誤:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://backend-dot-i7643225firenotes.appspot.com/notes. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). 

任何想法是什麼原因造成這種情況?我沒有觸及Google提供的代碼,但無論如何我都會加入它。它的其餘部分可以在這裏找到:

https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/appengine/standard/firebase/firenotes

main.js

// Copyright 2016, Google, Inc. 
// Licensed under the Apache License, Version 2.0 (the "License"); 
// you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at 
// 
// http://www.apache.org/licenses/LICENSE-2.0 
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
// See the License for the specific language governing permissions and 
// limitations under the License. 

$(function(){ 
    // This is the host for the backend. 
    // TODO: When running Firenotes locally, set to http://localhost:8081. Before 
    // deploying the application to a live production environment, change to 
    // https://backend-dot-<PROJECT_ID>.appspot.com as specified in the 
    // backend's app.yaml file. 
    var backendHostUrl = 'https://backend-dot-i7643225firenotes.appspot.com'; //localhost:8081 

    // Initialize Firebase 
    // TODO: Replace with your project's customized code snippet 
    var config = { 
    apiKey: "REMOVED", 
    authDomain: "REMOVED", 
    databaseURL: "https://<DATABASE_NAME>.firebaseio.com", 
    storageBucket: "<BUCKET>.appspot.com", 
    }; 

    // This is passed into the backend to authenticate the user. 
    var userIdToken = null; 

    // Firebase log-in 
    function configureFirebaseLogin() { 

    firebase.initializeApp(config); 

    // [START onAuthStateChanged] 
    firebase.auth().onAuthStateChanged(function(user) { 
     if (user) { 
     $('#logged-out').hide(); 
     var name = user.displayName; 

     /* If the provider gives a display name, use the name for the 
     personal welcome message. Otherwise, use the user's email. */ 
     var welcomeName = name ? name : user.email; 

     user.getToken().then(function(idToken) { 
      userIdToken = idToken; 

      /* Now that the user is authenicated, fetch the notes. */ 
      fetchNotes(); 

      $('#user').text(welcomeName); 
      $('#logged-in').show(); 

     }); 

     } else { 
     $('#logged-in').hide(); 
     $('#logged-out').show(); 

     } 
    // [END onAuthStateChanged] 

    }); 

    } 

    // [START configureFirebaseLoginWidget] 
    // Firebase log-in widget 
    function configureFirebaseLoginWidget() { 
    var uiConfig = { 
     'signInSuccessUrl': '/', 
     'signInOptions': [ 
     // Leave the lines as is for the providers you want to offer your users. 
     firebase.auth.GoogleAuthProvider.PROVIDER_ID, 

     //firebase.auth.FacebookAuthProvider.PROVIDER_ID, 
     //firebase.auth.TwitterAuthProvider.PROVIDER_ID, 
     //firebase.auth.GithubAuthProvider.PROVIDER_ID, 

     firebase.auth.EmailAuthProvider.PROVIDER_ID 
     ], 
     // Terms of service url 
     'tosUrl': '<your-tos-url>', 
    }; 

    var ui = new firebaseui.auth.AuthUI(firebase.auth()); 
    ui.start('#firebaseui-auth-container', uiConfig); 
    } 
    // [END configureFirebaseLoginWidget] 

    // [START fetchNotes] 
    // Fetch notes from the backend. 
    function fetchNotes() { 
    $.ajax(backendHostUrl + '/notes', { 
     /* Set header for the XMLHttpRequest to get data from the web server 
     associated with userIdToken */ 
     headers: { 
     'Authorization': 'Bearer ' + userIdToken 
     } 
    }).then(function(data){ 
     $('#notes-container').empty(); 
     // Iterate over user data to display user's notes from database. 
     data.forEach(function(note){ 
     $('#notes-container').append($('<p>').text(note.message)); 
     }); 
    }); 
    } 
    // [END fetchNotes] 

    // [START signOutBtn] 
    // Sign out a user 
    var signOutBtn =$('#sign-out'); 
    signOutBtn.click(function(event) { 
    event.preventDefault(); 

    //FirebaseAuth.getInstance().signOut(); 
    firebase.auth().signOut().then(function() { 
     console.log("Sign out successful"); 
    }, function(error) { 
     console.log(error); 
    }); 
    }); 
    // [END signOutBtn] 

    // [START saveNoteBtn] 
    // Save a note to the backend 
    var saveNoteBtn = $('#add-note'); 
    saveNoteBtn.click(function(event) { 
    event.preventDefault(); 

    var noteField = $('#note-content'); 
    var note = noteField.val(); 
    noteField.val(""); 

    /* Send note data to backend, storing in database with existing data 
    associated with userIdToken */ 
    $.ajax(backendHostUrl + '/notes', { 
     headers: { 
     'Authorization': 'Bearer ' + userIdToken 
     }, 
     method: 'POST', 
     data: JSON.stringify({'message': note}), 
     contentType : 'application/json' 
    }).then(function(){ 
     // Refresh notebook display. 
     fetchNotes(); 
    }); 

    }); 
    // [END saveNoteBtn] 

    configureFirebaseLogin(); 
    configureFirebaseLoginWidget(); 

}); 

main.py

# Copyright 2016 Google Inc. 
# 
# Licensed under the Apache License, Version 2.0 (the "License"); 
# you may not use this file except in compliance with the License. 
# You may obtain a copy of the License at 
# 
#  http://www.apache.org/licenses/LICENSE-2.0 
# 
# Unless required by applicable law or agreed to in writing, software 
# distributed under the License is distributed on an "AS IS" BASIS, 
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
# See the License for the specific language governing permissions and 
# limitations under the License. 

# [START app] 
import logging 

from flask import Flask, jsonify, request 
import flask_cors 
from google.appengine.ext import ndb 
import google.auth.transport.requests 
import google.oauth2.id_token 
import requests_toolbelt.adapters.appengine 

# Use the App Engine Requests adapter. This makes sure that Requests uses 
# URLFetch. 
requests_toolbelt.adapters.appengine.monkeypatch() 
HTTP_REQUEST = google.auth.transport.requests.Request() 

app = Flask(__name__) 
flask_cors.CORS(app) 


# [START note] 
class Note(ndb.Model): 
    """NDB model class for a user's note. 

    Key is user id from decrypted token. 
    """ 
    friendly_id = ndb.StringProperty() 
    message = ndb.TextProperty() 
    created = ndb.DateTimeProperty(auto_now_add=True) 
# [END note] 


# [START query_database] 
def query_database(user_id): 
    """Fetches all notes associated with user_id. 

    Notes are ordered them by date created, with most recent note added 
    first. 
    """ 
    ancestor_key = ndb.Key(Note, user_id) 
    query = Note.query(ancestor=ancestor_key).order(-Note.created) 
    notes = query.fetch() 

    note_messages = [] 

    for note in notes: 
     note_messages.append({ 
      'friendly_id': note.friendly_id, 
      'message': note.message, 
      'created': note.created 
     }) 

    return note_messages 
# [END query_database] 


# [START list_notes] 
@app.route('/notes', methods=['GET']) 
def list_notes(): 
    """Returns a list of notes added by the current Firebase user.""" 

    # Verify Firebase auth. 
    # [START verify_token] 
    id_token = request.headers['Authorization'].split(' ').pop() 
    claims = google.oauth2.id_token.verify_firebase_token(
     id_token, HTTP_REQUEST) 
    if not claims: 
     return 'Unauthorized', 401 
    # [END verify_token] 

    notes = query_database(claims['sub']) 

    return jsonify(notes) 
# [END list_notes] 


# [START add_note] 
@app.route('/notes', methods=['POST', 'PUT']) 
def add_note(): 
    """ 
    Adds a note to the user's notebook. The request should be in this format: 

     { 
      "message": "note message." 
     } 
    """ 

    # Verify Firebase auth. 
    id_token = request.headers['Authorization'].split(' ').pop() 
    claims = google.oauth2.id_token.verify_firebase_token(
     id_token, HTTP_REQUEST) 
    if not claims: 
     return 'Unauthorized', 401 

    # [START create_entity] 


    data = request.get_json() 

    # Populates note properties according to the model, 
    # with the user ID as the key name. 
    note = Note(
     parent=ndb.Key(Note, claims['sub']), 
     message=data['message']) 

    # Some providers do not provide one of these so either can be used. 
    note.friendly_id = claims.get('name', claims.get('email', 'Unknown')) 
    # [END create_entity] 

    # Stores note in database. 
    note.put() 

    return 'OK', 200 
# [END add_note] 


@app.errorhandler(500) 
def server_error(e): 
    # Log the error and stacktrace. 
    logging.exception('An error occurred during a request.') 
    return 'An internal error occurred.', 500 
# [END app] 
+0

.py文件中的CORS標頭在哪裏? – mplungjan

+0

我不確定這些是什麼?我需要包括這個嗎?這是谷歌自己的代碼,所以不知道爲什麼它不起作用。 – jb2003

回答

1

我已經試過the tutorial自己,我的一切工作正常,所以我想你可能會跳過一些步驟,或者你有一些錯誤的配置。

唯一明顯的區別我你的榜樣和我之間看,有以下幾種,所以你可以嘗試對其進行修改,以看到您的問題是否得到解決:

  • 我看到一些import error在我的代碼,所以我將這一行werkzeug==0.12.2添加到backend/requirements.txt文件中,該文件包含將要安裝的庫。 werzeug庫的最新版本已將一些依賴關係移至嵌套文件夾,這是爲什麼某些導入失敗的原因(您可以閱讀更多here)。然後,刪除lib文件夾並重新運行該命令以安裝庫pip install -r requirements.txt -t lib。在進行此修改之前,我遇到了與您的問題相同的問題,在我的應用程序中單擊保存按鈕時沒有發生任何事情,但在更改後,它工作正常。
  • 我的frontend/main.js文件中的config變量有一些您已刪除的其他字段。我有以下的參數this guide,並要我的火力地堡控制檯,點擊全面添加火力地堡到你的Web應用程序按鈕,如下複製內容:

config變量frontend/main.js

var config = { 
    apiKey: "<API_KEY>", 
    authDomain: "<PROJECT_ID>.firebaseapp.com", 
    databaseURL: "https://<PROJECT_ID>.firebaseio.com", 
    projectId: "<PROJECT_ID>", 
    storageBucket: "<PROJECT_ID>.appspot.com", 
    messagingSenderId: "<SOME_ID>" 
    }; 

至於其他方面,一切看起來都很好,只是我剛剛嘗試過使用firebase.auth.GoogleAuthProvider.PROVIDER_ID,而我已經刪除了其餘所有內容。我也在運行生產中的應用程序(App Engine標準版),而不是使用本地的開發服務器。我看了一下我的CORS配置過了,我沒有什麼特別的,只有幾行,你已經在你的代碼:

app = Flask(__name__) 
flask_cors.CORS(app) 

你應該嘗試的情侶我提供建議,並與回來有關錯誤的更多信息,如果它不斷出現。

相關問題