在catch块中调用抛出函数安全吗


exports.createUser = functions.https.onCall(async (data, _context) => {
const email = data.email;
const password = data.password;
try {
// First, create the user account.
const userRecord = await admin.auth().createUser({
email: email,
password: password,
});
// User successfully created, now update the database.
const userId = userRecord.uid;
const db = admin.firestore();
const batch = db.batch();
batch.create(
db.collection("userAccounts").doc(userId), {created: admin.firestore.FieldValue.serverTimestamp()},
);
await batch.commit();
// Database successfully updated, now return the newly-created userId.
return Promise.resolve(userId);
} catch (error) {
if (userId) {
// There was an error updating the database. However, a user was created
// beforehand. Therefore, delete the user before terminating the
// function.
admin.auth().deleteUser(userId); // <-- this throws
throw new functions.https.HttpsError("unknown", "Database error", error);
}
throw new functions.https.HttpsError("unknown", "Authentication error", error);
}
});

此函数执行两个异步任务:创建用户和更新数据库。如果数据库更新失败,我需要取消删除该用户。此函数在捕获数据库错误时删除catch块中的用户。但是,删除用户本身会引发。如果delete用户函数失败,这个错误会被这个catch块捕获并可能创建一个无限循环吗?如何最好地处理这种情况?

它不会创建一个无限循环,但也不会捕捉到错误——这是应该避免的。您需要使用另一个.catch,因为您还想在之后抛出不同的错误。

} catch (error) {
if (userId) {
// There was an error updating the database. However, a user was created
// beforehand. Therefore, delete the user before terminating the
// function.
admin.auth().deleteUser(userId)
.catch(() => {}); // Ignore errors thrown by deleteUser
throw new functions.https.HttpsError("unknown", "Database error", error);
}
throw new functions.https.HttpsError("unknown", "Authentication error", error);
}

您还应该确保传递给onCall的此回调的调用者捕获其异步错误,以避免未处理的拒绝。

尽管如此,这个过程还是很可疑的——故意调用一个你认为可能会抛出的函数似乎很奇怪。如果仅在createUser成功的情况下才需要调用deleteUser,那么检查userId看起来是正确的方法,但只有当变量在此时的作用域中时,这才有效,而事实并非如此。您可以将代码更改为:

exports.createUser = functions.https.onCall(async (data, _context) => {
const { email, password } = data;
let userId; // <---------------------------
try {
// First, create the user account.
const userRecord = await admin.auth().createUser({ email, password });
// User successfully created, now update the database.
userId = userRecord.uid; // <---------------------------
const db = admin.firestore();
const batch = db.batch();
batch.create(
db.collection("userAccounts").doc(userId), {created: admin.firestore.FieldValue.serverTimestamp()},
);
await batch.commit();
// Database successfully updated, now return the newly-created userId.
return userId; // don't need to wrap this in a Promise
} catch (error) {
if (userId) {
// There was an error updating the database. However, a user was created
// beforehand. Therefore, delete the user before terminating the
// function.
admin.auth().deleteUser(userId)
.catch(() => {}); // This SHOULDN'T throw, but just in case
throw new functions.https.HttpsError("unknown", "Database error", error);
}
throw new functions.https.HttpsError("unknown", "Authentication error", error);
}
});

或至

exports.createUser = functions.https.onCall(async (data, _context) => {
const { email, password } = data;
let userId;
try {
// First, create the user account.
const userRecord = await admin.auth().createUser({ email, password });
userId = userRecord.uid;
} catch (error) {
throw new functions.https.HttpsError("unknown", "Authentication error", error);
}
try {
// User successfully created, now update the database.
const db = admin.firestore();
const batch = db.batch();
batch.create(
db.collection("userAccounts").doc(userId), { created: admin.firestore.FieldValue.serverTimestamp() },
);
await batch.commit();
// Database successfully updated, now return the newly-created userId.
return userId;
} catch (error) {
// There was an error updating the database. However, a user was created
// beforehand. Therefore, delete the user before terminating the
// function.
admin.auth().deleteUser(userId);
throw new functions.https.HttpsError("unknown", "Database error", error);
}
});

最新更新