【问题标题】:I cannot get data from firebase database我无法从 firebase 数据库中获取数据
【发布时间】:2021-04-28 12:37:19
【问题描述】:

我是 firebase 的新手。我正在尝试使用 node.js 服务器将相关凭据发送到 firebase 从实时数据库中检索数据,但是在调用 once('value') 之后某些东西会被破坏:它返回的承诺永远不会得到已解决,服务器停止自行记录此消息:“进程退出,代码为 3221226505”。

我写了以下代码:

async function testFirebase1(firebaseCredentialsObj, path) {
  let firebase = require('firebase')
  firebase.initializeApp(firebaseCredentialsObj);
  var database = firebase.database();
  var ref = database.ref(path);
  console.log(ref.toString());
  try {

    // Attempt 1 
    var prom = await ref.once('value'); 
    const data = prom.;
    console.log('data ' + data)

    // Attempt 2
    prom.then((snapshot) => {
      console.log('snapshot ' + snapshot)
    }).catch((error) => { console.log(error)} )

  } catch (error) {
    console.log(error)
  }
}

没有错误被捕获。 我也尝试以管理员身份获取数据,但我得到了同样的失败结果

async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
  const admin=require('firebase-admin');
  const serviceAccount = serviceAccountKey;
  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: databaseURL
    });
    var db=admin.database();
    var userRef=db.ref(path);    
    const prom = await userRef.once('value');
    console.log(prom)
}

从 once() 方法返回的 Promise 保持悬而未决。这是它的日志:

[[PromiseStatus]]:'待定' [[PromiseValue]]:undefined

服务器应该以json格式获取数据库的数据并将其发送给客户端。 为什么会这样?

【问题讨论】:

  • 请附上您呼叫testFirebase1testFirebase3的代码。

标签: javascript node.js firebase firebase-realtime-database firebase-authentication


【解决方案1】:

根据您的代码,您将传统的 Promise 链接和 async/await 语法混合在一起,这会导致您感到困惑。

注意:在下面的sn-ps中,我使用了我在end of this answer描述的数据库查询编码风格。

SDK 初始化

首先,在 testFirebase1testFirebase3 中,您在函数中初始化默认 Firebase 应用实例。如果你只调用任何一个函数一次,你不会遇到任何问题,但是任何时候你再次调用它们,它们总是会抛出一个错误,说明应用程序已经初始化。为了解决这个问题,您可以使用以下函数延迟加载这些库:

function lazyFirebase(options, name = undefined) {
  const firebase = require('firebase');
  // alternatively use the Promise-based version in an async function:
  // const firebase = await import('firebase');

  try {
    firebase.app(name);
  } catch (err) {
    firebase.initializeApp(options, name);
  }
  return firebase;
}

function lazyFirebaseAdmin(options, name = undefined) {
  const admin = require('firebase-admin');
  // alternatively use the Promise-based version in an async function:
  // const admin = await import('firebase-admin');

  try {
    admin.app(name);
  } catch (err) {
    const cred = options.credential;

    if (typeof cred === "string") {
       options.credential = admin.credential.cert(cred)
    }

    admin.initializeApp(options, name);
  }
  return admin;
}

重要提示:上述函数都不会检查它们是否使用相同的options 对象来初始化它们。它只是假设它们是相同的配置对象。

纠正testFirebase1

testFirebase1 中,您正在初始化默认的 Firebase 应用实例,然后开始从数据库中获取数据的过程。因为您还没有从函数中的ref.once('value') 返回承诺,所以调用者将获得一个Promise<undefined>,该Promise<undefined> 在数据库调用完成之前解析。

async function testFirebase1(firebaseCredentialsObj, path) {
  let firebase = require('firebase')

  // bug: throws error if initializeApp called more than once
  firebase.initializeApp(firebaseCredentialsObj);

  // bug: using `var` - use `const` or `let`
  var database = firebase.database();
  var ref = database.ref(path);
  console.log(ref.toString());
  try {

    // Attempt 1 
    // bug: using `await` here, makes this a DataSnapshot not a Promise<DataSnapshot>
    //      hence `prom` should be `snapshot`
    // bug: using `var` - use `const` or `let`
    var prom = await ref.once('value');

    // bug: syntax error, assuming this was meant to be `prom.val()`
    const data = prom.;
    console.log('data ' + data)

    // Attempt 2
    // bug: a `DataSnapshot` doesn't have a `then` or `catch` method
    // bug: if `prom` was a `Promise`, you should return it here
    prom
      .then((snapshot) => {
        console.log('snapshot ' + snapshot)
      })
      .catch((error) => {
        console.log(error)
      })

  } catch (error) {
    console.log(error)
  }
}

纠正这些问题(并在处理 RTDB 查询时利用我的编码风格)给出:

async function testFirebase1(firebaseCredentialsObj, path) {
  const firebase = lazyFirebase(firebaseCredentialsObj);

  const snapshot = await firebase.database()
    .ref(path)
    .once('value');

  // returns data at this location
  return snapshot.val();
}

纠正testFirebase3

testFirebase3 中,您正在初始化默认的 Firebase Admin 应用实例并正确等待来自数据库的数据。因为您还没有从数据库返回数据,所以调用者将得到一个Promise&lt;undefined&gt;,当数据库调用完成但没有包含数据时,它会解析。

async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
  const admin = require('firebase-admin');

  // note: unnecessary line, either call `serviceAccountKey` `serviceAccount` or use `serviceAccountKey` as-is
  const serviceAccount = serviceAccountKey;

  // bug: throws error if initializeApp called more than once
  // bug: `firebaseCredentials` is unused
  // note: when initializing the *default* app's configuration, you
  //       should specify all options to prevent bugs when using
  //       `admin.messaging()`, `admin.auth()`, `admin.storage()`, etc
  //       as they all share the default app instance
  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: databaseURL
  });

  // bug: using `var` - use `const` or `let`
  var db=admin.database();
  var userRef=db.ref(path);

  // bug: using `await` here, makes this a DataSnapshot not a Promise<DataSnapshot>
  //      hence `prom` should be `snapshot`
  const prom = await userRef.once('value');
                                            
  // bug: logging a `DataSnapshot` object isn't useful because it
  //      doesn't serialize properly (it doesn't define `toString()`,
  //      so it will be logged as "[object Object]")
  console.log(prom)
}

纠正这些问题(并在处理 RTDB 查询时利用我的编码风格)给出:

async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
  const admin = lazyFirebaseAdmin({
    ...firebaseCredentials, // note: assuming `firebaseCredentials` is the complete app configuration,
    credential: serviceAccountKey,
    databaseURL: databaseURL
  });

  const snapshot = await admin.database()
    .ref(path)
    .once('value');

  return snapshot.val();
}

【讨论】:

  • @steam 我在对lazyFirebaseAdmin 的调用中发现了一个使用admin.credential.cert() 的错误。这现在已被替换为将serviceAccountKey 作为credential 本身传递,然后在lazyFirebaseAdmin() 内部将cert() 应用于它。
  • 很好用,非常感谢!我试过你的代码,它运行良好,但我需要删除“lazyFirebaseAdmin”方法中的类型控制,因为 typeof cred 不是字符串而是对象。
猜你喜欢
  • 1970-01-01
  • 2021-10-25
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
  • 1970-01-01
  • 1970-01-01
  • 2019-10-29
  • 2020-02-27
相关资源
最近更新 更多