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) 

你应该尝试的情侣我提供建议,并与回来有关错误的更多信息,如果它不断出现。

相关问题