首页 > 解决方案 > 使用 Cloud Functions 在 Firestore 数据库上存储来自外部 API 的数据

问题描述

我有一个在 Firebase 上运行的 Vue.js 应用程序,我使用 Firestore 作为数据库。该应用程序必须从另一个应用程序 (app2) 导入数据(客户端),但 app2 仅通过通过 POST 将 XML 代码发送到地址来导出。为了从 app2 接收 POST,我的应用使用了 Firebase Cloud Functions。

const xml2js = require("xml2js");
const functions = require("firebase-functions");
const cors = require("cors");
const express = require("express");
const app = express(); 
const admin = require("firebase-admin");

const db = admin.initializeApp().firestore();

function parseXml(xml) {
    return new Promise((resolve, reject) => {
        xml2js.parseString(xml, { explicitArray: false }, (err, result) => {
            if (err) {
                reject(err);
            } else {
                resolve(result);
            }
        });
    });
}

app.use(cors({ origen: true }));
    app.post("/", async (request, response) => {
    let xml = request.body.XMLRecords.toString();
    const clientes = db.collection("clientes");
    xml = xml.replace(/&(?!(?:apos|quot|[gl]t|amp);|#)/g, "&").trim();
    xml = " " + xml;

    console.log("Prep XML:");
    console.log(xml);

    let parsXml = await parseXml(xml);
    console.log("parsXML");
    console.log(parsXml);

    Array.from(parsXml.Records.Clientes.Cliente).forEach(async cli => {
        if (cli !== null) {
            delete cli.$;
            const docRef = clientes.doc(cli.CliCodigo);
            console.log("cli " + cli.CliCodigo);
            console.log(cli);
            const writeResult = await docRef.set(cli);
            console.log("Cliente " + cli.CliCodigo + " salvo");
        }
    });

    response.send("Received.");
});

exports.apiClientes = functions.https.onRequest((request, response) => {
    if (!request.path) {
        request.url = `/${request.url}`; // prepend '/' to keep query params if any
    }
    return app(request, response);
});

该应用程序确实收到了请求并且能够处理它,但是当我尝试与 Firestore 数据库通信时,该函数停止发送console.log()数据并且不会将数据保存到数据库中。我究竟做错了什么?

标签: javascriptfirebasegoogle-cloud-firestoregoogle-cloud-functionsxml2js

解决方案


这很可能是因为在您的Array.from(parsXml.Records.Clientes.Cliente).forEach()循环中您正在执行几个异步set()操作,但您在这些异步操作完成之前response.send("Received.");返回了 response( ) 。

通过这样做response.send("Received.");,您向运行 Cloud Function 的实例表明它可以终止它,但在大多数情况下,对 Firestore 的异步写入并未完成。

您需要正确处理set()方法调用返回的 Promises,如下(未经测试):

//....
const promises = [];

Array.from(parsXml.Records.Clientes.Cliente).forEach(cli => {
    if (cli !== null) {
        delete cli.$;   
        const docRef = clientes.doc(cli.CliCodigo);

        promises.push(docRef.set(cli));
    }
});

await Promise.all(promises);
response.send("Received.");
//....

因此,我们使用Promise.all()并行执行所有set()方法调用。Promise.all()方法返回一个单一的Promise,当所有作为可迭代对象(即promises数组)传递的 Promise 都已实现时,该 Promise 就实现了。因此,当您发回响应时,您确信所有异步工作都已完成,向 Cloud Function 平台表明它可以安全地终止您的 Cloud Function。


推荐阅读