代码之家  ›  专栏  ›  技术社区  ›  gdejohn

Firebase云函数中的嵌套HTTP请求

  •  0
  • gdejohn  · 技术社区  · 7 年前

    const functions = require("firebase-functions");
    const admin = require("firebase-admin");
    admin.initializeApp();
    
    const request = require('request');
    
    exports.foo = functions.https.onRequest(
        (req, res) => {
            var ref = admin.database().ref("/foo");
            var options = {
                url: "https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=****",
                json: true
            };
            return request(
                options,
                (error, response, body) => {
                    if (error) {
                        console.log(JSON.stringify(error));
                        return res.status(500).end();
                    }
                    if ("results" in body) {
                        for (var i = 0; i < body.results.length; i++) {
                            var result = body.results[i];
                            if ("name" in result &&
                                "description" in result &&
                                "group" in result &&
                                "urlname" in result.group
                            ) {
                                var groupOptions = {
                                    url: "https://api.meetup.com/" + result.group.urlname + "?sign=true&photo-host=public&key=****",
                                    json: true
                                };
                                var categoryResult = request(
                                    groupOptions,
                                    (groupError, groupResponse, groupBody) => {
                                        if (groupError) {
                                            console.log(JSON.stringify(error));
                                            return null;
                                        }
                                        if ("category" in groupBody &&
                                            "name" in groupBody.category
                                        ) {
                                            return groupBody.category.name;
                                        }
                                        return null;
                                    }
                                );
                                if (categoryResult) {
                                    var event = {
                                        name: result.name,
                                        description: result.description,
                                        category: categoryResult
                                    };
                                    ref.push(event);
                                }
                            }
                        }
                        return res.status(200).send("processed events");
                    } else {
                        return res.status(500).end();
                    }
                }
            );
        }
    );
    

    函数崩溃了,log说:

    Error: Reference.push failed: first argument contains a function in property 'foo.category.domain._events.error' with contents = function (err) {
          if (functionExecutionFinished) {
            logDebug('Ignoring exception from a finished function');
          } else {
            functionExecutionFinished = true;
            logAndSendError(err, res);
          }
        }
        at validateFirebaseData (/user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/index.node.cjs.js:1436:15)
        at /user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/index.node.cjs.js:1479:13
        at Object.forEach (/user_code/node_modules/firebase-admin/node_modules/@firebase/util/dist/index.node.cjs.js:837:13)
        at validateFirebaseData (/user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/index.node.cjs.js:1462:14)
        at /user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/index.node.cjs.js:1479:13
        at Object.forEach (/user_code/node_modules/firebase-admin/node_modules/@firebase/util/dist/index.node.cjs.js:837:13)
        at validateFirebaseData (/user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/index.node.cjs.js:1462:14)
        at /user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/index.node.cjs.js:1479:13
        at Object.forEach (/user_code/node_modules/firebase-admin/node_modules/@firebase/util/dist/index.node.cjs.js:837:13)
        at validateFirebaseData (/user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/index.node.cjs.js:1462:14)
    

    3 回复  |  直到 7 年前
        1
  •  1
  •   R. Wright    7 年前

    我怀疑这个问题是由于回调。当您使用firebase函数时,导出的函数应该等待所有内容执行,或者在所有内容完成执行后返回解析的承诺。在这种情况下,导出的函数将在其余执行完成之前返回。

    const functions = require("firebase-functions");
    const admin = require("firebase-admin");
    admin.initializeApp();
    
    const request = require("request-promise-native");
    
        exports.foo = functions.https.onRequest(async (req, res) => {
        const ref = admin.database().ref("/foo");
        try {
            const reqEventOptions = {
                url:
                    "https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=xxxxxx",
                json: true
            };
            const bodyEventRequest = await request(reqEventOptions);
            if (!bodyEventRequest.results) {
                return res.status(200).end();
            }
            await Promise.all(
                bodyEventRequest.results.map(async result => {
                    if (
                        result.name &&
                        result.description &&
                        result.group &&
                        result.group.urlname
                    ) {
                        const event = {
                            name: result.name,
                            description: result.description
                        };
    
                        // get group information
                        const groupOptions = {
                            url:
                                "https://api.meetup.com/" +
                                result.group.urlname +
                                "?sign=true&photo-host=public&key=xxxxxx",
                            json: true
                        };
    
                        const categoryResultResponse = await request(groupOptions);
                        if (
                            categoryResultResponse.category &&
                            categoryResultResponse.category.name
                        ) {
                            event.category = categoryResultResponse.category.name;
                        }
    
                        // save to the databse
                        return ref.push(event);
                    }
                })
            );
            return res.status(200).send("processed events");
        } catch (error) {
            console.error(error.message);
        }
    });
    

    快速概述更改-

    • 变量使用const和let vs.var;这改进了变量的范围
    • 不要像if(is good){do good things}那样进行检查,而要使用if(isbad){returnsome error}do good thin。这使得代码更易于阅读,并防止了很多嵌套的if,而您不知道它们的结尾
    • 使用Promise.all()以便并行地检索每个事件的类别
        2
  •  1
  •   Renaud Tarnec    7 年前

    您应该在代码中实现两个主要更改:

    • request 请求 request-promise 为了正确地链接不同的异步事件(参见Doug对您的问题的评论)
    • 因为您将使用 请求承诺 Promise.all() 为了等所有的承诺都解决了再发回回复。对Firebase的不同调用也是如此 push()

    因此,按照以下行修改代码应该是可行的。

    我让你修改它,这样你就可以得到 name description 用于构建 event 对象。中项目的顺序 results 数组与 promises 名称 描述 在内部 results.forEach(groupBody => {})


    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp();
    
    var rp = require('request-promise');
    
    exports.foo = functions.https.onRequest((req, res) => {
      var ref = admin.database().ref('/foo');
      var options = {
        url:
          'https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=****',
        json: true
      };
      rp(options)
        .then(body => {
          if ('results' in body) {
            const promises = [];
            for (var i = 0; i < body.results.length; i++) {
              var result = body.results[i];
              if (
                'name' in result &&
                'description' in result &&
                'group' in result &&
                'urlname' in result.group
              ) {
                var groupOptions = {
                  url:
                    'https://api.meetup.com/' +
                    result.group.urlname +
                    '?sign=true&photo-host=public&key=****',
                  json: true
                };
    
                promises.push(rp(groupOptions));
              }
            }
            return Promise.all(promises);
          } else {
            throw new Error('err xxxx');
          }
        })
        .then(results => {
          const promises = [];
    
          results.forEach(groupBody => {
            if ('category' in groupBody && 'name' in groupBody.category) {
              var event = {
                name: '....',
                description: '...',
                category: groupBody.category.name
              };
              promises.push(ref.push(event));
            } else {
              throw new Error('err xxxx');
            }
          });
          return Promise.all(promises);
        })
        .then(() => {
          res.send('processed events');
        })
        .catch(error => {
          res.status(500).send(error);
        });
    });
    
        3
  •  0
  •   gdejohn    7 年前

    我做了一些修改,让它与节点8一起工作 package.json :

    "engines": {
        "node": "8"
    }
    

    answer 以及一些Firebase云函数示例代码。

    const functions = require("firebase-functions");
    const admin = require("firebase-admin");
    admin.initializeApp();
    
    const request = require("request-promise-native");
    
    exports.foo = functions.https.onRequest(
        async (req, res) => {
            var ref = admin.database().ref("/foo");
            var options = {
                url: "https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=****",
                json: true
            };
            await request(
                options,
                async (error, response, body) => {
                    if (error) {
                        console.error(JSON.stringify(error));
                        res.status(500).end();
                    } else if ("results" in body) {
                        for (var i = 0; i < body.results.length; i++) {
                            var result = body.results[i];
                            if ("name" in result &&
                                "description" in result &&
                                "group" in result &&
                                "urlname" in result.group
                            ) {
                                var groupOptions = {
                                    url: "https://api.meetup.com/" + result.group.urlname + "?sign=true&photo-host=public&key=****",
                                    json: true
                                };
                                var groupBody = await request(groupOptions);
                                if ("category" in groupBody && "name" in groupBody.category) {
                                    var event = {
                                        name: result.name,
                                        description: result.description,
                                        category: groupBody.category.name
                                    };
                                    await ref.push(event);
                                }
                            }
                        }
                        res.status(200).send("processed events");
                    }
                }
            );
        }
    );