简体   繁体   English

对 Firebase Cloud Functions (Spark Tier) 中外部 API 的 HTTP 请求被拒绝

[英]HTTP request to an external API in Firebase Cloud Functions (Spark Tier) refused

I am trying to call an external resource from the web and load the results into Dialogflow using the NodeJS Client V2 and Cloud Functions.我正在尝试从 Web 调用外部资源,并使用 NodeJS Client V2 和 Cloud Functions 将结果加载到 Dialogflow 中。

I have tried multiple combinations of this code, using promises, external functions, etc. No luck.我已经尝试了这段代码的多种组合,使用了 Promise、外部函数等。没有运气。

Test1测试1

function myIntent(conv) {
            // console.log('conv ', conv)

            const config ={
                "headers": {'X-App-Token': dataSFAPITOken},
                "Content-Type": "application/json"
            }
            const url = "http://data.sfgov.org/resource/cuks-n6tp.json";
            return new Promise((resolve, reject) => {

                axios
                    .get(url)
                    .then(response => {
                        console.log(response);
                        conv.ask('I got your data back')
                        return resolve(response)
                    })
                    .catch(error => {
                        console.log(error);
                        conv.ask('Sorry! Something went wrong')
                        return reject(error)
                    });
            })

    }
 app.intent('My Intent', myIntent);  
 exports.myAgent = functions.https.onRequest(app);

Error错误

This is the error that I get on the Cloud Functions Dashboard, after I invoke myIntent .这是我在调用myIntent后在 Cloud Functions Dashboard 上得到的错误。

{ Error: getaddrinfo ENOTFOUND data.sfgov.org data.sfgov.org:80
    at errnoException (dns.js:28:10)
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:76:26)
  code: 'ENOTFOUND',
  errno: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'data.sfgov.org',
  host: 'data.sfgov.org',
  port: 80,
  config: 
   { adapter: [Function: httpAdapter],
     transformRequest: { '0': [Function: transformRequest] },
     transformResponse: { '0': [Function: transformResponse] },
     timeout: 0,
     xsrfCookieName: 'XSRF-TOKEN',
     xsrfHeaderName: 'X-XSRF-TOKEN',
     maxContentLength: -1,
     validateStatus: [Function: validateStatus],
     headers: 
      { Accept: 'application/json, text/plain, */*',
        'User-Agent': 'axios/0.18.0' },
     method: 'get',
     url: 'http://data.sfgov.org/resource/cuks-n6tp.json',
     data: undefined },
  request: 
   Writable {
     _writableState: 
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        needDrain: false,
        ending: false,
        ended: false,
        finished: false,
        decodeStrings: true,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: true,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 0,
        prefinished: false,
        errorEmitted: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: true,
     domain: null,
     _events: 
      { response: [Function: handleResponse],
        error: [Function: handleRequestError] },
     _eventsCount: 2,
     _maxListeners: undefined,
     _options: 
      { protocol: 'http:',
        maxRedirects: 21,
        maxBodyLength: 10485760,
        path: '/resource/cuks-n6tp.json',
        method: 'get',
        headers: [Object],
        agent: undefined,
        auth: undefined,
        hostname: 'data.sfgov.org',
        port: null,
        nativeProtocols: [Object],
        pathname: '/resource/cuks-n6tp.json' },
     _redirectCount: 0,
     _requestBodyLength: 0,
     _requestBodyBuffers: [],
     _onNativeResponse: [Function],
     _currentRequest: 
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 6,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: true,
        upgrading: false,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: false,
        sendDate: false,
        _removedHeader: {},
        _contentLength: 0,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Object],
        connection: [Object],
        _header: 'GET /resource/cuks-n6tp.json HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.18.0\r\nHost: data.sfgov.org\r\nConnection: close\r\n\r\n',
        _headers: [Object],
        _headerNames: [Object],
        _onPendingData: null,
        agent: [Object],
        socketPath: undefined,
        timeout: undefined,
        method: 'GET',
        path: '/resource/cuks-n6tp.json',
        _ended: false,
        _redirectable: [Circular],
        parser: null },
     _currentUrl: 'http://data.sfgov.org/resource/cuks-n6tp.json' },
  response: undefined }  

Test2测试2

const url = "data.sfgov.org";
var options = {
    protocol:'http:',
    host: url,
    port : 8080,
    path:'/resource/cuks-n6tp',
    headers: {
        'X-App-Token': dataSFAPITOken,
        "Content-Type": "application/json"
    }
};
http.get(options, (http_res) => {
    // initialize the container for our data
    var data = "";

    // this event fires many times, each time collecting another piece of the response
    http_res.on("data", (chunk) =>{
        // append this chunk to our growing `data` var
        data += chunk;
    });

    // this event fires *one* time, after all the `data` events/chunks have been gathered
    http_res.on("end", () => {
        // you can use res.send instead of console.log to output via express
        console.log(data);
    });
});

Here is the link to the official documentation HERE .这里是链接到官方文档这里

The crazy thing about this is that below is a similar implementation on the browser and it works.令人疯狂的是,下面是浏览器上的类似实现,并且可以正常工作。 As a matter of fact, I don't need an API key to query the data.事实上,我不需要 API 密钥来查询数据。 I can simply copy/paste the link.我可以简单地复制/粘贴链接。

$.ajax({
    url: "https://data.sfgov.org/resource/cuks-n6tp.json",
    type: "GET",
    data: {
      "$limit" : 50,
      "$$app_token" : "APPTOKEN"
    }
}).done(function(data) {
  // alert("Retrieved " + data.length + " records from the dataset!");
  console.log(data);
  document.getElementById('data').innerHTML = JSON.stringify(data, null,2)
});

But for some reasons, I can't make it work with Dialogflow NodeJS Client V2.但由于某些原因,我无法使其与 Dialogflow NodeJS Client V2 一起使用。 I am pretty sure in V1, I can make it work.我很确定在 V1 中,我可以让它工作。

My migration to V2 is a little painful.我迁移到 V2 有点痛苦。 Please help.请帮忙。

Thanks.谢谢。

The free Spark plan on Firebase Cloud Functions doesn't allow calls to domains outside Google. Firebase Cloud Functions 上的免费Spark计划不允许调用 Google 以外的域。

You will need to upgrade to one of the paid plans such as the Flame or Blaze plan, which requires a credit card on file.您需要升级到其中一种付费计划,例如FlameBlaze计划,这需要存档信用卡。 For low levels of usage, however, you won't be charged on Blaze .但是,对于使用量较低的情况,您无需为Blaze

The image below shows Spark , Flame , and Blaze .下图显示了SparkFlameBlaze Notice the Google services only for Spark .请注意Google services only适用于SparkGoogle services only https://firebase.google.com/pricing/ https://firebase.google.com/pricing/

在此处输入图片说明

Update, Nov 2020 2020 年 11 月更新

Note that the Flame plan is no longer available.请注意, Flame计划不再可用。 So if you want external access, you'll need to use the Blaze plan.因此,如果您想要外部访问,则需要使用Blaze计划。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM