Firebase 函数接收对象而不是字符串

Posted

技术标签:

【中文标题】Firebase 函数接收对象而不是字符串【英文标题】:Firebase Functions receive Object instead of String 【发布时间】:2020-01-14 21:04:15 【问题描述】:

忍受我。我花了一个月的时间来回答这个问题:我已经使用 Firebase 数据库和 Firebase 函数大约一年了。我已经让它工作了......但前提是我将消息文本作为字符串发送。问题是现在我希望收到一个 OBJECT,但我不确定如何在 FireBaseMessage 中执行此操作。

我之前的结构:

messages
   T9Vh5cvUcbqC8IEZowBpJC3
      ZWfn7876876ZGJeSNBbCpPmkm1
           message



"messages": 
        ".read": true,
      "$receiverUid": 
        "$senderUid": 
          "$message": 
            ".read": true,
            ".write": "auth.uid === $senderUid"

我的监听器功能是这样的:

exports.sendMessage = functions.database.ref('/messages/receiverUid/senderUid/message')

这是有问题的......出于各种原因。也就是说,如果旧消息是“嘿”,然后同一个人再次写“嘿”……那么原来的消息就会被覆盖。

所以我的新结构更像这样:

messages
  -LkVcYqJoEroWpkXZnqr
      body: "genius grant"
      createdat: 1563915599253
      name: "hatemustdie"
      receiverUid: "TW8289372984KJjkhdsjkhad"
      senderUid: "yBNbs9823789KJkjahsdjkas"

具体写成:

mDatabase.child("messages").push().setValue(message);

...我只是不确定如何写出那个函数。

我的意思是......理想情况下......它会是这样的:

exports.sendMessage = functions.database.ref('/messages/receiverUid/senderUid/msgID/msgOBJECT')

...但我只是不确定 Firebase 函数是如何读取这个新结构的。

现在我像这样推送到数据库:

mDatabase.child("messages").child(guid).child(user_Id).push().setValue(msgObject).addOnSuccessListener(this, new OnSuccessListener<Void>() 
                                @Override
                                public void onSuccess(@NonNull Void T) 
                                    Log.d("MessageActivity", "Message Sent");

基本上我只想接收消息对象...其中包含所有内容...当它从通知到达时...并且能够轻松解析正文、日期、用户 ID 等。

有人能解释一下解决这个问题的正确方法吗?

UPATE应要求提供完整的云功能:

exports.sendMessage = functions.database.ref('/messages/receiverUid/senderUid/msgId/message')
    .onWrite(async (change, context) => 
      const message = context.params.message;
      // const messageId = context.params.messageId;
      const receiverUid = context.params.receiverUid;
      const senderUid = context.params.senderUid;
      // If un-follow we exit the function.
      if (!change.after.val()) 
        return console.log('Sender ', senderUid, 'receiver ', receiverUid, 'message ', message);
      
      console.log('We have a new message: ', message, 'for: ', receiverUid);

      // Get the list of device notification tokens.
      const getDeviceTokensPromise = admin.database()
          .ref(`/users/$receiverUid/notificationTokens`).once('value');

      // Get the follower profile.
      const getSenderProfilePromise = admin.auth().getUser(senderUid);

      // The snapshot to the user's tokens.
      let tokensSnapshot;

      // The array containing all the user's tokens.
      let tokens;

      const results = await Promise.all([getDeviceTokensPromise, getSenderProfilePromise]);
      tokensSnapshot = results[0];
      const sender = results[1];

        // Check if there are any device tokens.
        if (!tokensSnapshot.hasChildren()) 
          return console.log('There are no notification tokens to send to.');
        
        console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
        console.log('Fetched sender profile', sender);
//        console.log('David you're looking for the following UID:', followerUid);

        // Notification details.
        const payload = 
          notification: 
            title: `$sender.displayName sent you a message.`,
            body: message,
            tag: senderUid
          ,
        //  'data':  'fuid': followerUid 
          data:  
            type: 'message', 
            name: sender.displayName
          
        ;
      console.log('David you are looking for the following message:', message);
        // Listing all tokens as an array.
      tokens = Object.keys(tokensSnapshot.val());
      // Send notifications to all tokens.
      const response = await admin.messaging().sendToDevice(tokens, payload);
      // For each message check if there was an error.
      const tokensToRemove = [];
      response.results.forEach((result, index) => 
        const error = result.error;
        if (error) 
          console.error('Failure sending notification to', tokens[index], error);
          // Cleanup the tokens who are not registered anymore.
          if (error.code === 'messaging/invalid-registration-token' ||
              error.code === 'messaging/registration-token-not-registered') 
            tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
          
        
      );
        return Promise.all(tokensToRemove);
  );

【问题讨论】:

展示你完整的云功能 刚刚贴在上面。 @YashKrishan 【参考方案1】:

由于您现在将发送者和接收者的 UID 存储在消息中,因此您的云函数的声明将需要更改。

而不是这个:

exports.sendMessage = functions.database.ref('/messages/receiverUid/senderUid/msgId/message').onWrite(async (change, context) => 

您需要触发:

exports.sendMessage = functions.database.ref('/messages/messageId').onWrite(async (change, context) => 

因此,通过此更改,您的代码将在写入 /messages 的每条消息上触发。

现在您“只”需要获取发送者和接收者的 UID。由于您不再可以从context 获取它们,您将改为从change 获取它们。具体来说,change.after 包含数据库中存在的数据快照写入完成之后。因此(只要您不删除数据),您可以通过以下方式获取 UID:

const receiverUid = change.after.val().receiverUid;
const senderUid = change.after.val().senderUid;

当然,你也会从那里得到实际的信息:

const message = change.after.val().message;

并且以防万一您需要消息 ID(它在数据库中写入的 -L... 键):

const messageId = change.after.val().messageId;

【讨论】:

哇...我不知道其他人是怎么想出来的。现在检查。稍后会报告! @YashKrishan 您的解决方案也是正确的,因此我对此表示赞同。当我们都按下回车键时,我只是输入了更多内容。 :) @mysticcola 我从documentation on Realtime Database triggers 获得了大部分信息,并通过查看functions-samples repo,这是一个伟大的代码示例的宝库。 完全理解并感谢。但是,我怀疑 OP 在 RTD 中构建对象的方式。这是在这么多层中构建它的正确方法吗:/messages/receiverUid/senderUid/msgId/message? 我有一种感觉,这绝对不是一个好习惯。减少层有助于更多地处理 JSON 等格式的数据。【参考方案2】:

您只需要在 messageId 上触发:

exports.sendMessage = functions.database.ref('/messages/messageId').onWrite((change, context) => 
      const changedData = change.after.val(); // This will have the complete changed data
      const message = change.after.val().message;  // This will contain the message value
      ......
);

详细说明弗兰克的回答: 您无法从 const message = context.params.message; 这样的上下文中获取数据,因为上下文中不再存在这些参数。

【讨论】:

这是否意味着我也必须更改 java ? (mDatabase.child("messages").child(guid).child(user_Id).push().setValue(message)) 还有可能还有上面定义的数据库结构? 不,我猜不是因为您已经修改了该代码以适应新格式。 我将其更改为“mDatabase.child("messages").push().setValue(message)”。它有点干净。此外,它更好地匹配:functions.database.ref('/messages/messageId')。感谢您的所有帮助! 你不会碰巧知道如何从“push()”获取 id 吧?它以前工作过......但现在 mDatabase2 = FirebaseDatabase.getInstance().getReference("messages"); mDatabase2.getKey() 只返回“消息”而不是实际的键。 一种方法是首先像这样存储密钥:***.com/a/43362516/8259771 然后使用它。

以上是关于Firebase 函数接收对象而不是字符串的主要内容,如果未能解决你的问题,请参考以下文章

当使用多个规则时,规则应该返回一个字符串或布尔值,而不是接收到的“对象”

如何让我的 Amazon Redshift UDF 接收字符串而不是列表?

在 Firebase 中插入 JSON 显示为字符串,而不是显示为树

我怎样才能设置一个id而不是字符串

如何从firebase数据库中检索数据作为字符串而不是字典并将其添加到数组中?

SpringMVC @RequestBody接收Json对象字符串