从 Flutter 调用 Firebase 可调用函数时无法处理错误
Can't handle errors when calling a Firebase Callable Function from Flutter
我正在从 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
)。
它停在 class 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 中),以下内容将打印到控制台:
Got this error:
[firebase_functions/failed-precondition] Error on purpose.
#0 StandardMethodCodec.decodeEnvelope
package:flutter/…/services/message_codecs.dart:597
#1 MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:158
<asynchronous suspension>
#2 MethodChannelHttpsCallable.call
package:cloud_functions_platform_interface/…/method_channel/method_channel_https_callable.dart:23
<asynchronous suspension>
#3 HttpsCallable.call
package:cloud_functions/src/https_callable.dart:35
Got this doc id: null
通过查看您的输出日志和代码,很明显您的第一个错误处理程序正在为您解决或处理错误,基于此行:
I/flutter (21112): Got this error: I/flutter (21112): [firebase_functions/failed-precondition] Error on purpose.
由于来自 Firebase 函数的错误由第一个错误处理程序处理(或“解决”),其他两个错误处理程序没有理由 运行,因为它们没有收到任何错误。这使得从您的按钮执行的 Future 被解析,尽管具有 null
值。
在这种情况下你可以做的是 rethrow
你的第一个错误处理程序中的错误作为 recommended to do in Dart, so it can be dealt with in the second handler, and rethrow it once more to the third handler. I believe in this scenario the second handler is redundant, and you could rethrow the error from the first handler to the third handler. catchError()
clauses already work 作为 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
省略 rethrow/throw 语句:
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();
}
我正在从 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
)。
它停在 class 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 中),以下内容将打印到控制台:
Got this error:
[firebase_functions/failed-precondition] Error on purpose.
#0 StandardMethodCodec.decodeEnvelope package:flutter/…/services/message_codecs.dart:597
#1 MethodChannel._invokeMethod package:flutter/…/services/platform_channel.dart:158
<asynchronous suspension>
#2 MethodChannelHttpsCallable.call package:cloud_functions_platform_interface/…/method_channel/method_channel_https_callable.dart:23
<asynchronous suspension>
#3 HttpsCallable.call package:cloud_functions/src/https_callable.dart:35
Got this doc id: null
通过查看您的输出日志和代码,很明显您的第一个错误处理程序正在为您解决或处理错误,基于此行:
I/flutter (21112): Got this error: I/flutter (21112): [firebase_functions/failed-precondition] Error on purpose.
由于来自 Firebase 函数的错误由第一个错误处理程序处理(或“解决”),其他两个错误处理程序没有理由 运行,因为它们没有收到任何错误。这使得从您的按钮执行的 Future 被解析,尽管具有 null
值。
在这种情况下你可以做的是 rethrow
你的第一个错误处理程序中的错误作为 recommended to do in Dart, so it can be dealt with in the second handler, and rethrow it once more to the third handler. I believe in this scenario the second handler is redundant, and you could rethrow the error from the first handler to the third handler. catchError()
clauses already work 作为 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
省略 rethrow/throw 语句:
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();
}