我的 Cloud 功能停止使用 Flutter Web 有啥重大变化吗?

Posted

技术标签:

【中文标题】我的 Cloud 功能停止使用 Flutter Web 有啥重大变化吗?【英文标题】:Did anything major change that my Cloud functions stopped working with flutter web?我的 Cloud 功能停止使用 Flutter Web 有什么重大变化吗? 【发布时间】:2021-08-16 14:04:34 【问题描述】:

我曾经运行 Firebase Cloud Functions。但是在将我的整个代码库重构为听起来空安全之后,云功能停止工作(遗憾的是,我无法在时间轴的哪个点重现)..

pubspec.yaml

    dependencies:
      flutter:
        sdk: flutter
      firebase_core: ^1.0.2
      firebase_auth: ^1.0.1
      cloud_firestore: ^1.0.4
      cloud_functions: ^1.1.0
      ...

网页/index.html

...
  <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-functions.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-auth.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.19.1/firebase-firestore.js"></script>
    
  <script>
    // Your web app's Firebase configuration
    var firebaseConfig = 
      apiKey: "<myApiKey>",
      authDomain: "<my-project>.firebaseapp.com",
      databaseURL: "https://<my-project>.firebaseio.com",
      projectId: "<my-project>",
      storageBucket: "<my-project>.appspot.com",
      messagingSenderId: "<myMessageSenderId>",
      appId: "<myAppId>"
    ;
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
    firebase.functions().useFunctionsEmulator("http://10.0.2.2:5001");
  </script>
  
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

函数/index.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const  UserPropertyValue  = require('firebase-functions/lib/providers/analytics');
admin.initializeApp();

exports.setRoles = functions.https.onCall((data, context) => 
    let userId = null;
    let userCustomClaimsAdmin = false;
    let userCustomClaimsEditor = false;

    // get user and update custom claim
    return admin.auth().getUserByEmail(data.email).then(user => 

        userId = user.uid;
        const currentCustomClaims = (user.customClaims == undefined) ?  : user.customClaims;
        switch (data.role) 
            case 'admin':
                currentCustomClaims.admin = (data.permission == 'grant') ? true : false;
            break;
            case 'editor':
                currentCustomClaims.editor = (data.permission == 'grant') ? true : false;
            break;
            default:
                return;
        

        userCustomClaimsAdmin = currentCustomClaims.admin;
        userCustomClaimsEditor = currentCustomClaims.editor;

        return admin.auth().setCustomUserClaims(userId,
            currentCustomClaims
        );
        ).then(() => 
            // Update User record in Firestore
            return admin.firestore().collection("users").doc(userId).update(
                isAdmin: userCustomClaimsAdmin,
                isEditor: userCustomClaimsEditor,
            );
        ).then(() => 
            return 
                message: 'Success'
            
        )
        .catch(err => 
            console.log(err.toString());
        );
);

最后我调用函数:

...
   final HttpsCallable setRoleCallable = FirebaseFunctions.instance
        .httpsCallable('setRoles',
            options:
                HttpsCallableOptions(timeout: const Duration(seconds: 10)));
...
              try 
                final HttpsCallableResult result = await setRoleCallable.call(
                  <String, dynamic>
                    'email': "<emailOfUserToBeChanged>",
                    'role': "<selectedRole>",
                    'permission': "<givenAccess>"
                  ,
                );
                print(result.data);
               on FirebaseFunctionsException catch (e) 
                print('caught firebase functions exception');
                print(e.code);
                print(e.message);
                print(e.details);
               catch (e) 
                print('caught generic exception');
                print(e);
              

那个调用(在 localhost 和 10.0.2.2 上的模拟函数)结束于

caught firebase functions exception
internal
internal
null

在此期间我错过了什么变化吗?我在 Firebase 文档中找不到有关此主题的任何内容。

也许在某个我还没有意识到的时候发生了一些小变化..

【问题讨论】:

【参考方案1】:

嗯,Cloud Functions 的一个重大变化是您现在必须拥有付费 Firebase 计划才能使用云功能,因为他们遗憾地从免费层中删除了 Cloud Functions .

【讨论】:

谢谢,这个提示可能对未来的读者有用。在我的情况下,我为该项目使用 Blaze-Plan :)【参考方案2】:

在您的 Cloud Function 中,您无需等待异步操作完成后再发回响应。有关此关键方面的更多详细信息,请参阅doc。

棘手的是它会产生一些“不稳定”的行为(有时有效,有时无效),可以解释如下:

在某些情况下,您的云函数会在异步操作完成之前终止,如上述文档中所述。

但是,在某些其他情况下,Cloud Functions 平台可能不会立即终止您的 CF,从而为完成异步操作提供足够的时间。

所以你的印象是“云功能停止与 Flutter Web 一起工作”,而实际上,有时它可以工作,而其他一些时候则不行......


此外,请注意setCustomUserClaims() 方法返回Promise&lt;void&gt; 而不是user,因此您需要为userId 和声明保留一组全局变量,以便从一个then() 传递它阻止对方。


所以以下应该可以解决问题(未经测试):

exports.setRoles = functions.https.onCall((data, context) => 
    console.log('user to change email: ' + data.email);

    let userId = null;
    let userCustomClaimsAdmin = false;
    let userCustomClaimsEditor = false;

    // get user and update custom claim
    return admin.auth().getUserByEmail(data.email)
        .then(user => 

            userId = user.uid;   //  the setCustomUserClaims() method returns a Promise<void> not a user !!

            const currentCustomClaims = (user.customClaims == undefined) ?  : user.customClaims;
            switch (data.role) 
                case 'admin':
                    currentCustomClaims.admin = (data.permission == 'grant') ? true : false;
                    break;
                case 'editor':
                    currentCustomClaims.editor = (data.permission == 'grant') ? true : false;
                    break;
                default:
                    return;
                    break;
            

            // Here you need to adapt the value of userCustomClaimsAdmin and userCustomClaimsEditor
            userCustomClaimsAdmin = ...
            userCustomClaimsEditor = ...

            // See return below !!!!
         
            return admin.auth().setCustomUserClaims(user.uid,
                currentCustomClaims
            );

        )
        .then(() => 
            // See return below  !!!!
            return admin.firestore().collection("users").doc(userId).update(
                isAdmin: (userCustomClaimsAdmin) ? user.customClaims.admin : false,
                isEditor: (userCustomClaimsEditor) ? user.customClaims.editor : false,
            );
        )
        .then(() => 
            return 
                message: 'Success'
            
        )
        .catch(err => 
            console.log(err.toString());
            // !!!! See the doc: https://firebase.google.com/docs/functions/callable#handle_errors
        );
);

【讨论】:

非常感谢。我将相应地更改代码并稍后发布结果... 好吧,我更改了 index.js(也更新了这篇文章),但错误/结果保持不变。 你需要适配部分//Here you need to adapt the value... 我想,我做到了?!我用 currentCustomClaims 的值设置全局变量。否则我在这里很无助。你的意思是?也许我只是没有做对.. 你应该用一些logging来调试代码。【参考方案3】:

好吧,我正在开发一个 Flutter-Web 项目。我使用的是 cloud-functions,而不是 cloud-functions-web。

在我的 main.dart 中缺少使用函数模拟器的指令:

...
Future<void> main() async 
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  FirebaseFunctions.instance
      .useFunctionsEmulator(origin: 'http://localhost:5001'); // this was missing
  runApp(MyApp());
...

它曾经可以工作,因为我的 index.html 中已经有该指令

...
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
    firebase.functions().useFunctionsEmulator("http://10.0.2.2:5001");
...

尽管如此,它现在可以工作了。

【讨论】:

以上是关于我的 Cloud 功能停止使用 Flutter Web 有啥重大变化吗?的主要内容,如果未能解决你的问题,请参考以下文章

Cloud Firestore 连接我的 Flutter Windows 应用

添加 cloud_firebase 包后,我的 Flutter App 没有运行

Flutter 应用程序的 Firebase 通知突然停止

Flutter/cloud-firestore“任务已经完成”异常

Flutter 和 Cloud Firestore 获取问题

使用 Flutter 向 Cloud Firestore 添加对象