无法处理从 Flutter 调用 Firebase 可调用函数时出现的错误



我正在从Flutter调用一个Firebase可调用函数,尽管我已经尝试了几乎所有的方法,但我仍然无法处理可调用函数引发的错误。

我的可调用函数如下:

import * as functions from "firebase-functions";
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
export const myFunction = functions.https.onCall((data, context) => {
//Get data passed to the function
const someData = data.someData;

//Run transaction
return db.runTransaction(async (transaction: any) => {
//Get data
const docRef = db.collection("myCollection").doc("myDocId");
const docResults = await transaction.get(docRef);
const docData = docResults.data();
//Run validations:
if(1 != 0){
throw new functions.https.HttpsError('failed-precondition', "Error on purpose");
}

//Create a new doc
const newDocRef = db.collection('myCollection').doc();
transaction.set(newDocRef, {someData: "myData",});
return {newDocId: newDocRef.id}
});
});

然后我在Flutter中创建了一个Future来调用我的可调用函数:

import 'package:cloud_functions/cloud_functions.dart';
Future<String> callMyFunction({String someData,}) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable(
'myFunction',  options: HttpsCallableOptions(timeout: Duration(seconds: 5)));
try {
return callable.call({
'someData': someData,
}).then((results) {
return results.data['newDocId'].toString();
}).catchError(handleError); //First error handler
} catch (error) {
//Second error handler
print("Missed the first error handler.");
handleError(error);
return null;
}
}
void handleError(error) {
print("Got this error:");
print(error);
}

最后,我用另一个错误处理程序:从TextButton调用我的未来

import 'package:flutter/material.dart';
import 'package:myApp/callableFuture.dart';
class myWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TextButton(
child: Text("Press me"),
onPressed: () {
callMyFunction(
someData: "Hi",
).catchError((error) {
//This is my third error handler
print("Missed first two error handlers. Got this error:");
print(error);
}).then((newDocId) =>
print("Got this doc id: $newDocId"));
},
);
}
}

当我调用我的函数并得到一个错误时,我的代码立即跳到Flutter的method_channel_https_callable.dart(位于.../flutter/.pub-cache/hosted/pub.dartlang.org/cloud_functions_platfrom_interface-5..0.15/lib/src/method_channel/method_channel_https_callable.dart(并停止。

它在函数call的类MethodChannelHttpsCallable处停止,就在这里:

try {
//...some code...
if (result is Map) {
return Map<String, dynamic>.from(result);
} else {
return result;
}
} catch (e, s) {
throw convertPlatformException(e, s); //My code stops right here.
}

最后,一旦我点击Step Over(在VSCode中(,以下内容将打印到控制台:

收到此错误:

[firebase_functions/failed preconditions]故意出错。

#0标准方法Codec.decodeEnvelope包裹:flutter/…/services/message_codecs.dart:597

#1 MethodChannel_invokeMethod软件包:flutter/…/services/platform_channel.dart:158

<异步暂停>

#2方法ChannelHttpsCallable.call软件包:cloud_functions_platform_interface/…/method_channel/meth_channel_https_callable.dart:23

<异步暂停>

#3 HttpsCallable.call包:cloud_functions/src/https_callable.dart:35

获取此文档id:null

从您的输出日志和代码中可以清楚地看到,您的第一个错误处理程序是基于以下行为您解决或处理错误:

I/flutter (21112): Got this error: I/flutter (21112): [firebase_functions/failed-precondition] Error on purpose.

由于来自Firebase函数的错误由第一个错误处理程序处理(或"解决"(,其他两个错误处理进程没有理由运行,因为它们没有收到任何错误。这使得从您的按钮执行的Future被解析,尽管其值为null

在这种情况下,您可以像在Dart中建议的那样,在第一个错误处理程序中rethrow错误,这样它就可以在第二个处理程序中处理,并再次将其重新抛出到第三个处理程序。我相信在这种情况下,第二个处理程序是多余的,您可以将错误从第一个处理程序重新拖到第三个处理程序。catchError()子句在Dart中已经作为try/catch块工作。

Firebase功能

exports.myFuncEmu = functions.https.onCall((data,context) => {
const testData = data.text;
if(testData == "throwEx"){
throw new functions.https.HttpsError('failed-precondition', "Error on purpose");
}
console.log(testData);
return `Finished: ${testData}`
});

Flutter Future

Future<String?> runFunction(String text) async {
await Firebase.initializeApp();
FirebaseFunctions.instance.useFunctionsEmulator('localhost', 5001);
HttpsCallable callableFunc =
FirebaseFunctions.instance.httpsCallable("myFuncEmu");
try {
return await callableFunc.call(
{"text": text},
).then((res) {
return res.data.toString();
}).catchError(handleError); //Handler from callable function
} catch (err) {
print("Missed the first error handler"); //Try/catch block handler
rethrow; //Throws here so it can be caught by Future handler in button widget
return null;
}
}
String handleError(err) {
print("Caught error: ");
print(err);
throw Exception(err); //Throws here so it can be caught by try/catch handler
return "";
}

颤振按钮

child: ElevatedButton( //Swapped order of then and catcherror clauses
onPressed: () {
runFunction("throwEx")
.then((val) => print("Final result: ${val}"))
.catchError((err) {
print(
"Missed first two error handlers"); //Handler from Future function
print(err);
});
},
child: const Text(
"RUN",
style: TextStyle(color: Colors.white),
),
),

添加所需的throw/rethrow语句,我们可以通过错误处理程序正确地重定向此错误,以便在发现错误后阻止代码执行:

Caught error:
[firebase_functions/failed-precondition] Error on purpose
Missed the first error handler
Missed first two error handlers
Exception: [firebase_functions/failed-precondition] Error on purpose

省略rethrow/sthrow语句:

Caught error:
[firebase_functions/failed-precondition] Error on purpose
Final result: //Incorrectly continues code, seems to not be handling errors

我真的不确定为什么,但这解决了问题:

我改变了调用可调用函数的未来:

import 'package:cloud_functions/cloud_functions.dart';
Future<String> callMyFunction({String someData,}) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable(
'myFunction',  options: HttpsCallableOptions(timeout: Duration(seconds: 5)));
try {
return callable.call({
'someData': someData,
}).then((results) {
return results.data['newDocId'].toString();
}).catchError(handleError); //First error handler
} catch (error) {
//Second error handler
print("Missed the first error handler.");
handleError(error);
return null;
}
}
void handleError(error) {
print("Got this error:");
print(error);
}

到此:

import 'package:cloud_functions/cloud_functions.dart';
Future<String> callMyFunction({String someData,}) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable(
'myFunction',  options: HttpsCallableOptions(timeout: Duration(seconds: 5)));
HttpsCallableResult results;
try {
results = await callable.call({
'someData': someData,
});
} catch (error) {
print("Got this error:");
print(error.code);
print(error.message);
return null;
}
return results.data['newDocId'].toString();
}

相关内容

  • 没有找到相关文章

最新更新