我正在从 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
)。
它停在班级
MethodChannelHttpsCallable
的功能call
,就在这里:
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-precondition] 故意错误。
#0 StandardMethodCodec.decodeEnvelope 包:flutter/…/services/message_codecs.dart:597
#1 MethodChannel._invokeMethod 包:flutter/…/services/platform_channel.dart:158
#2 MethodChannelHttpsCallable.call 包:cloud_functions_platform_interface/…/method_channel/method_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
值。
在这种情况下你可以做的是
rethrow
在第一个错误处理程序中按照Dart中的建议这样做,这样它就可以在第二个处理程序中处理,并再次将其重新抛出到第三个处理程序。我相信在这种情况下,第二个处理程序是多余的,您可以将错误从第一个处理程序重新抛出到第三个处理程序。 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}`
});
颤振未来
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
省略重新抛出/抛出语句:
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();
}