使用Firebase云功能

时间:2017-07-08 14:56:28

标签: javascript firebase google-cloud-functions

我正在尝试在新用户注册之前在数据库中找到重复的用户名,然后根据我的建议使用Firebase云功能对用户进行身份验证,因为.queryEqual(toValue)本地不会每次都有效。我现在无法理解的是如何在我的应用程序中使用它。这是我部署的云功能:

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database. 
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.uniqueUsername = functions.https.onRequest((req, res) => {
    const username = req.query.username
    admin.database().ref('users').orderByChild('username').equalTo(username).once('value').then(snap => {
        // if the child exists, then the username is taken
        if (snap.exists()) {
            res.send('username not available');
        } else {
            res.send('username available');
        }
    }); 
});

equalTo(username)是从UITextField中挑选的值,然后与数据库中的值进行比较。我得到的错误是:

Error: Query.equalTo failed: First argument contains undefined in property 'users'
    at Error (native)
    at Ae (/user_code/node_modules/firebase-admin/lib/database/database.js:105:67)
    at ze (/user_code/node_modules/firebase-admin/lib/database/database.js:104:400)
    at W.h.Jf (/user_code/node_modules/firebase-admin/lib/database/database.js:142:60)
    at exports.uniqueUsername.functions.https.onRequest (/user_code/index.js:10:60)
    at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:26:47)
    at /var/tmp/worker/worker.js:649:7
    at /var/tmp/worker/worker.js:633:9
    at _combinedTickCallback (internal/process/next_tick.js:67:7)
    at process._tickDomainCallback (internal/process/next_tick.js:122:9)

我在浏览器中使用URL时遇到的错误是:

Error: could not handle the request

我做错了什么?接下来我该怎么办?

1 个答案:

答案 0 :(得分:2)

从该错误消息中,您将undefined的值传递给equalTo函数。

在执行查询(以及任何查询)之前,您希望使用"fail-fast"方法。在这种情况下,如果req.query.usernamefalsy,则返回错误。

exports.uniqueUsername = functions.https.onRequest((req, res) => {
  const username = req.query.username;
  if (!username) {
    res.status(400).json({error: 'Missing username GET parameter'});
    return;
  }
  // rest of your code.
}

更好的方法

对于任何需要唯一性的键,您需要使用索引。在您的数据库中,它看起来类似于:

"/usernames": {
  "bob": "userId1",
  "frank": "userId2",
  "c00lguy": "userId3"
}

为了检查用户名是否免费,我建议先让用户登录并进行身份验证。这可以确保我们有一个用户名与用户名相关联。一旦他们登录,检查他们是否还没有设置用户名,如果他们没有,请使用某种形式的对话框/输入提示他们。

要检查用户名的可用性并在“单个”操作中声明它,您可以调用以下代码:(内部不是单个操作)

var desiredUsername = '...'; // from the form.
var userId = '...'; // the current logged in user's ID (normally from user.uid)
var ref = firebase.database().ref('/usernames').child(desiredUsername);
ref.transaction(function (currentData) {
  if (currentData !== null && currentData !== userId) {
    return; // returns undefined. Which aborts the operation because there is data here. (someone owns that username other than the current user).
  }
  // if here, username is available or owned by this user.
  return userId; // returning a value will write it to the database at the given location.
})
.then(function(transactionResult) {
  if (transactionResult.committed) {
    // username is now owned by current user
    // do something
  } else {
    // username already taken.
    // do something
  }
})
.catch(function (error) {
  // if here, an error has occured
  // (probably a permissions error, check database rules)
  // do something
});

此代码段使用Transaction操作,允许您从数据库中读取值,然后立即对其执行操作。在原始代码中,在您调用uniqueUsername函数并声明该用户名之间,其他人可以这样做。使用交易可以通过在用户知道其免费的情况下声明用户名来尽量减少执行此操作的机会窗口。

此方法比搜索数据库更快,并允许您使用简单查询按用户名查找用户。

以下代码是此类查询的示例,并将为无人认领的用户名返回null

function findUserIdByUsername(username) {
  if (!username) {
    return Promise.reject('username is falsy');
  }
  firebase.database().ref('/usernames').child(username).on('value')
  .then(snapshot => {
    if (!snapshot.exists()) {
      return null; // no user with that username
    }
    return snapshot.val(); // the owner's user ID
  });
}

结合原始代码,它看起来像这样:

exports.uniqueUsername = functions.https.onRequest((req, res) => {
  const username = req.query.username;
  if (!username) {
    res.status(400).json({error: 'Missing username GET parameter'});
    return;
  }
  var ownerID = findUserIdByUsername(username);
  res.json({taken: (ownerID !== null)}); // Returns {"taken": "true"} or {"taken": "false"}
}