onRequest 与 onCall 返回 null

时间:2021-04-21 19:07:57

标签: javascript node.js firebase google-cloud-firestore google-cloud-functions

请帮我弄清楚下面的 onCall 和 onRequest google 函数在返回行为上的区别。

  • onCall,问题是:除了第一次返回(如下所述)之外,所有返回都返回 null。 db 条目和其余代码工作正常。只是没有退货问题。

  • onRequest,每次返回都完美无缺。 db 条目和其余代码也可以正常工作。

您将看到的两者比较相同,但我似乎根本无法使其正常工作。任何关于如何让我的回报为 onCall 工作(并更好地构建它)的建议将不胜感激。

我热衷于坚持异步等待(而不是承诺)。使用 Node.js 12。我在 Flutter 中调用 onCall,不知道这是否与问题相关。

onCall:

  exports.applyUserDiscount = functions.https.onCall(async (data, context) => {
  if (!context.auth) return {message: "Authentication Required!", code: 401};

  const uid = context.auth.uid;
  const discountCode = data["discountCode"];
  const cartTotal = data["cartTotal"];

  try {
    return await db.collection("discountCodes").where("itemID", "==", discountCode).limit(1).get()
        .then(async (snapshot) => {
          if (snapshot.empty) {
            return "doesNotExist"; // The only return that works.
          } else { // Everything else from here onwards returns null.
            snapshot.forEach(async (doc) => {
              if (doc.data().redeemed == true) {
                return "codeUsed";
              } else {
                const newCartTotal = cartTotal - doc.data().discountAmount;
                if (newCartTotal < 0) {
                  return "lessThanTotal";
                } else {
                  doc.ref.update({
                    redeemed: true,
                    uid: uid,
                    redeemDate: fireDateTimeNow,
                  });
                  await db.collection("userdata").doc(uid).set({
                    cartDiscount: admin.firestore.FieldValue.increment(-doc.data().discountAmount),
                  }, {merge: true});
                  return doc.data().discountAmount.toString();
                }
              }
            });
          }
        });
  } catch (error) {
    console.log("Error:" + error);
    return "error";
  }
});

onRequest:

exports.applyUserDiscount = functions.https.onRequest(async (req, res) => {

  const uid = req.body.uid;
  const discountCode = req.body.discountCode;
  const cartTotal = req.body.cartTotal;

  try {
    return await db.collection("discountCodes").where("itemID", "==", discountCode).limit(1).get()
        .then(async (snapshot) => {
          if (snapshot.isempty) {
            res.send("doesNotExist");
          } else {
            snapshot.forEach(async (doc) => {
              if (doc.data().redeemed == true) {
                res.send("codeUsed");
              } else {
                const newCartTotal = cartTotal - doc.data().discountAmount;
                if (newCartTotal < 0) {
                  res.send("lessThanTotal");
                } else {
                  doc.ref.update({
                    redeemed: true,
                    uid: uid,
                    redeemDate: fireDateTimeNow,
                  });
                  await db.collection("userdata").doc(uid).set({
                    cartDiscount: admin.firestore.FieldValue.increment(-doc.data().discountAmount),
                  }, {merge: true});
                  res.send(doc.data().discountAmount.toString());
                }
              }
            });
          }
        });
  } catch (error) {
    console.log(error);
    res.send("error");
  }
});

1 个答案:

答案 0 :(得分:1)

在查看您的代码时需要注意以下几点:

  • 您不应在 async/await 循环中使用 forEach。问题是没有等待传递给 forEach() 的回调,请参阅更多解释 herehere但是,在您的情况下,您不需要遍历 QuerySnapshot ,因为它只包含一个文档。您可以使用 docs 属性返回 QuerySnapshot 中所有文档的数组,并获取第一个(也是唯一的)元素。
  • 您将 then()async/await 混淆,这是不推荐的。
  • 对于“错误”情况,我建议使用 throw exceptions,例如 doesNotExistcodeUsedlessThanTotal,但这取决于您的选择。例如,lessThanTotal 案例是错误或标准业务案例的事实是有争议的......因此,如果您更喜欢发送“文本”响应,我建议将此响应封装在一个对象中一个属性:在您的前端,响应将始终具有相同的格式。

因此,以下内容应该可以解决问题。请注意,我发送回带有 response 元素的对象,包括可能被视为错误的情况。如上所述,您可以在这些情况下抛出异常。

exports.applyUserDiscount = functions.https.onCall(async (data, context) => {
    if (!context.auth) ... //See https://firebase.google.com/docs/functions/callable#handle_errors

    const uid = context.auth.uid;
    const discountCode = data["discountCode"];
    const cartTotal = data["cartTotal"];

    try {
        const snapshot = await db.collection("discountCodes").where("itemID", "==", discountCode).limit(1).get();

        if (snapshot.empty) {

            //See https://firebase.google.com/docs/functions/callable#handle_errors

        } else {
            const uniqueDoc = snapshot.docs[0];

            if (uniqueDoc.data().redeemed == true) {
                return { response: "codeUsed" };
            } else {
                const newCartTotal = cartTotal - uniqueDoc.data().discountAmount;
                if (newCartTotal < 0) {
                    return { response: "lessThanTotal" };
                } else {
                    await uniqueDoc.ref.update({   // See await here!!
                        redeemed: true,
                        uid: uid,
                        redeemDate: fireDateTimeNow,
                    });
                    await db.collection("userdata").doc(uid).set({
                        cartDiscount: admin.firestore.FieldValue.increment(-uniqueDoc.data().discountAmount),
                    }, { merge: true });
                    return {
                        response: uniqueDoc.data().discountAmount.toString()
                    }
                }
            }

        }

    } catch (error) {
        console.log("Error:" + error);
        return "error";
    }
});