如何将参数从 Dart 传递到用 Typescript 编写的 Cloud 函数,它还给了我错误代码 UNAUTHENTICATED

Posted

技术标签:

【中文标题】如何将参数从 Dart 传递到用 Typescript 编写的 Cloud 函数,它还给了我错误代码 UNAUTHENTICATED【英文标题】:How to pass arguments from Dart to Cloud functions written in Typescript and it also gives me error code UNAUTHENTICATED 【发布时间】:2020-08-07 14:24:02 【问题描述】:

Dart 函数(将token 传递给sendToDevice):

Future<void> _sendNotification() async 
  CloudFunctions functions = CloudFunctions.instance;
  HttpsCallable callable = functions.getHttpsCallable(functionName: "sendToDevice");

  callable.call(
    'token': await FirebaseMessaging().getToken(),
  );

index.ts 我定义了sendToDevice 方法的文件。

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();

const fcm = admin.messaging();

export const sendToDevice = functions.firestore
  .document('users/uid')
  .onCreate(async snapshot => 

    const payload: admin.messaging.MessagingPayload = 
      notification: 
        title: 'Dummy title',
        body: `Dummy body`,
        click_action: 'FLUTTER_NOTIFICATION_CLICK'
      
    ;

    return fcm.sendToDevice(tokens, payload); // how to get tokens here passed from above function?
  
);

问题:

    如何接收从我的 Dart 函数 _sendNotification 传递到 Typescript 的 sendToDevice 函数的令牌。

    当我直接在 index.ts 文件中传递令牌时,我遇到了这个异常:

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] 未处理的异常:PlatformException(functionsError, Cloud function failed with exception., code: UNAUTHENTICATED, details: null, message: UNAUTHENTICATED)

谁能解释一下我是否应该在这里验证某些东西?命令 firebase login 显示我已经登录。我对 Typescript 很陌生,所以请耐心等待这些愚蠢的问题。

【问题讨论】:

您是否尝试在创建新文档时触发此功能?如果是这样,您为什么再次从客户端调用相同的函数? @StewieGriffin 是的,实际上这就是我要找的东西,因为我不知道打字稿,不知道这段代码在做什么。 那么您应该将您的设备令牌存储在您的数据库中,以便在触发函数时查询它们,而不是将它们从客户端传递给函数。流程应该是这样的:创建文档 -> 触发云函数 -> 函数从 db 查询令牌 -> 它推送通知 【参考方案1】:

这里有几件事要提:

1st 'getHttpsCallable' 中使用的函数必须由 https 触发器触发(参考here)。这里我们有一个由firestore document create触发的函数,所以它不起作用。

2nd 你的函数没有参数,但是你用参数调用它。如果您需要使用参数调用云函数的示例,您可以在pud.dev找到它

3rd 目前我没有机会玩它,但我认为如果你用token参数实现https触发功能,你应该可以传递这个参数。

希望对你有帮助!

更新:

根据doc https 触发函数必须用functions.https 创建。文档中有一个很好的例子。要以这种方式触发功能,您可以在传递所需数据时添加请求正文。

【讨论】:

感谢您的建议,但很抱歉它们不是很有用。例如,您说getHttpsCallable 应该由https 触发,您能告诉我我应该在代码中的哪个位置进行更改吗? 第二,您提供的示例没有显示他们如何处理index.js文件中传递的参数,他们只提供了Dart代码,我的代码类似于它,你能告诉我我该怎么做在index.js 里面这样做? 对不起@iKeepChangingName 我以为很清楚,我添加了对Google文档的引用,希望对您有所帮助!【参考方案2】:

您的 Flutter 代码似乎是正确的,但 Cloud Function 有问题。

sendToDevice 函数不是可调用函数。它是一个Cloud Firestore Triggers,它只意味着在创建与users/uid 匹配的文档时自动调用。

相反,您需要创建一个Callable Function,见下文

export const sendToDevice = functions.https
  .onCall(async (data) => 
    const  token  = data; // Data is what you'd send from callable.call

    const payload: admin.messaging.MessagingPayload = 
      notification: 
        title: 'Dummy title',
        body: `Dummy body`,
        click_action: 'FLUTTER_NOTIFICATION_CLICK'
      
    ;

    return fcm.sendToDevice(token, payload);
  
);

【讨论】:

感谢您的回答,我正在使用您的代码,但我收到此错误Unhandled Exception: PlatformException(functionsError, Cloud function failed with exception., code: UNAUTHENTICATED, details: null, message: UNAUTHENTICATED),我仔细检查了我的token 不是null 在这种情况下,我建议执行一些调试步骤,以确保首先,仔细检查您的凭据是否在同一个 Firebase 项目中。其次,删除 FCM 部分只是为了测试调用的函数。访问您的 Firebase 控制台并尝试使用您获得的令牌发送通知。您可能还想查看 Cloud Function Dashboard 上的日志记录【参考方案3】:

您已经创建了一个数据库触发器,您应该创建一个如下所示的可调用函数

exports.sendToDevice = functions.https.onCall(async (data, context) => 
    const payload: admin.messaging.MessagingPayload = 
        notification: 
          title: 'Dummy title',
          body: `Dummy body`,
          click_action: 'FLUTTER_NOTIFICATION_CLICK'
        
      ;

      return await fcm.sendToDevice(data.token, payload);
  );

【讨论】:

【参考方案4】:

此答案可能无法解决您的问题,但可以让您尝试一些事情,并且您将在此过程中学习。不幸的是,我无法让可调用的 https 与模拟器一起工作。我可能会很快提交一个关于它的 github 问题。根据我尝试的本地 URL,flutter 应用程序不断收到不同类型的无法识别的错误。


很高兴您已经解决了其中一个问题:您使用的是文档触发器 (onCreate) 而不是 https 可调用对象。但是现在,您正在运行一个 https 可调用对象,并且 Flutter 应用程序需要直接与您的函数进行通信。以后,你可以在本地运行函数模拟器,并做很多console.log'ing 来了解它是否真的被触发了。

我有几个问题/你可以尝试:

您的用户是否已登录 Flutter 应用程序? FirebaseAuth.instance.currentUser() 会告诉你。 iosandroid 都会出现这个问题吗? 将一些日志添加到您的 typescript 函数中,然后重新部署。通过 StackDriver 或终端 firebase functions:log --only sendToDevice 阅读最新日志。 (sendToDevice 是你的可调用函数名) 您是否正在部署到云端并使用最新的功能部署进行测试?您实际上可以使用local emulator 进行测试。在 Android 上,URL 是10.0.2.2:5001,如上所示。您还需要在终端中运行adb reverse tcp:5001 tcp:5001。如果你在云端,那么firebase login 没关系,我认为你的函数应该已经有了凭据。

调用模拟器https callable:

HttpsCallable callable = CloudFunctions.instance
    .useFunctionsEmulator(origin: "http://10.0.2.2:5001")
    .getHttpsCallable(functionName: "sendToDevice");

而iOS你需要遵循here的解决方案。

我发现了一个错误。您至少应该在等待 promise 解决的地方执行return await fcm.sendToDevice(),否则云函数运行时将在您的函数解决之前终止它。或者,为了调试,您可以将它保存到一个变量中,而不是在您的云函数中返回 sendToDevice,然后 console.log'd 它。你会看到它实际上是一个尚未真正解决的承诺(或 dart 术语中的未来)。

 const messagingDevicesResponse: admin.messaging.MessagingDevicesResponse = await fcm.sendToDevice(
    token,
    payload
  );
  console.log( messagingDevicesResponse );
  return;

【讨论】:

【参考方案5】:

公开函数

问题与凭据有关。如果问题得到解决,您可以更改 CF 和 sheck 的安全策略。了解如何管理 CF 上的权限here

【讨论】:

以上是关于如何将参数从 Dart 传递到用 Typescript 编写的 Cloud 函数,它还给了我错误代码 UNAUTHENTICATED的主要内容,如果未能解决你的问题,请参考以下文章

Dart:如何通过流将数据从一个进程传递到另一个进程

如何通过 Dart 中的 onTap 函数将值传递到另一个屏幕?

如何将任意数据从 Dart 聚合物 Web 组件传递给单击事件处理函数

Flutter/Dart 如何将索引从 Listview.builder 传递到项目小部件

如何在 Dart 中将 Navigator 函数作为参数传递?

如何将 obj-c NSDictionary 传递给 dart