UrlFetchApp 请求在菜单函数中失败,但在自定义函数中失败(连接到外部 REST API)
Posted
技术标签:
【中文标题】UrlFetchApp 请求在菜单函数中失败,但在自定义函数中失败(连接到外部 REST API)【英文标题】:UrlFetchApp request fails in Menu Functions but not in Custom Functions (connecting to external REST API) 【发布时间】:2020-11-11 09:56:12 【问题描述】:我正在使用以下函数连接到外部 API (Binance),使用 Google Apps 脚本检索 JSON 数组(市场价格)。这个简单的查询 url 在浏览器中运行良好(不需要 API 密钥):
function getMyArray()
var url ="https://api.binance.com/api/v3/ticker/price"; // works perfectly in browser
var params = "method" : "get", "muteHttpExceptions":true ;
var response = UrlFetchApp.fetch(url, params);
var array = JSON.parse(response.getContentText());
return array;
但是,当我尝试在 Google Apps 脚本中运行该函数时,情况就不同了:
-
脚本编辑器:在脚本编辑器中运行简单实用,但我收到
403 error
“请求被阻止”
菜单功能:从添加到电子表格 UI 的菜单项调用该功能 => 相同 403 error
自定义函数:编辑任何单元格并输入=getMyArray()
=> 请求有效,我可以使用 Logger 跟踪数组
为什么我的简单请求在从菜单或脚本编辑器调用时会被阻止,是否可以更改?谢谢
【问题讨论】:
根据您的情况,我提出了一种解决方法作为答案。如果这不是您期望的方向,我很抱歉。 【参考方案1】:当UrlFetchApp
被自定义函数和脚本编辑器使用时,我认为区别在于是否使用IPv6,而IPv4的地址每次运行都会改变。在这种情况下,脚本编辑器和自定义菜单的结果是相同的。我认为这可能是您的问题的原因。但我不确定我的猜测是否正确。因此,在这个答案中,我想提出以下解决方法。
-
使用脚本将公式
=getMyArray()
放入单元格中。
通过这种方式,将值检索到单元格中。
使用脚本从单元格中检索值。
清除 put 公式。
通过这个流程,我认为你的目标可以实现。
示例脚本如下。
示例脚本:
在此脚本中,作为测试,=getMyArray()
被放入活动工作表上的单元格“A1”,并从单元格中检索值。当您使用它时,请在脚本编辑器和自定义菜单中运行函数main()
。这样,可以将值检索到array
。
function getMyArray()
var url = "https://api.binance.com/api/v3/ticker/price";
var params = "method": "get", "muteHttpExceptions": true;
var response = UrlFetchApp.fetch(url, params);
return response.getContentText();
// Please run this function by the script editor and the custom menu.
function main()
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A1");
range.setFormula("=getMyArray()");
SpreadsheetApp.flush();
var value = range.getValue();
range.clearContent();
var array = JSON.parse(value);
console.log(array)
参考资料:
Custom Functions in Google Sheets setFormula() flush()补充:
https://httpbin.org/get
的响应值如下。
用于测试的示例脚本:
function sample()
var url = "https://httpbin.org/get";
var res = UrlFetchApp.fetch(url);
console.log(res.getContentText())
return res.getContentText();
结果:
模式 1. 使用脚本编辑器运行脚本。
"args": ,
"headers":
"Accept-Encoding": "gzip,deflate,br",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; +https://script.google.com; id: ###)",
"X-Amzn-Trace-Id": "Root=###"
,
"origin": "### IPV6 ###, ### IPV4 ###", // or "### IPV4 ###, ### IPV4 ###"
"url": "https://httpbin.org/get"
当您使用 IPV6 时,origin
是 "### IPV6 ###, ### IPV4 ###"
。但是当你使用 IPV4 时,origin
是"### IPV4 ###, ### IPV4 ###"
。
在这种情况下,无法从https://api.binance.com/api/v3/ticker/price
检索到正确的值。
模式 2. 使用自定义函数运行脚本。
在这种情况下,=sample()
被放入单元格并检索值。
"args": ,
"headers":
"Accept-Encoding": "gzip,deflate,br",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; +https://script.google.com; id: ###)",
"X-Amzn-Trace-Id": "Root=###"
,
"origin": "### IPV4 ###",
"url": "https://httpbin.org/get"
在这种情况下,可以从https://api.binance.com/api/v3/ticker/price
检索到正确的值。
模式 3. 使用 OnEdit 事件触发器运行脚本。
UrlFetchApp
与自定义函数一起使用时,无需授权。但是当UrlFetchApp
与OnEdit事件触发器一起使用时,通过授权需要安装触发器。我认为这个授权可能会出现这个问题。所以我比较了这个。
当UrlFetchApp
与可安装的 OnEdit 事件触发器一起使用时,将检索以下结果。
"args": ,
"headers":
"Accept-Encoding": "gzip,deflate,br",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; +https://script.google.com; id: ###)",
"X-Amzn-Trace-Id": "Root=###"
,
"origin": "### IPV4 ###",
"url": "https://httpbin.org/get"
此结果与上述模式 2 相同。
在这种情况下,可以从https://api.binance.com/api/v3/ticker/price
检索到正确的值。
结果:
包括User-Agent
在内的标头对于所有模式都是相同的。
从模式2和模式3来看,与谷歌端授权无关。
检索带有 IPV4 的 WHOIS 时,返回相同的结果。
当origin
为"### IPV4 ###, ### IPV4 ###"
时,第二个IPV4 是Google 的IP 地址。
从上面的结果来看,所有模式的不同之处在于origin
的值是1还是2。
【讨论】:
这肯定是一种解决方法,但我仍然很想知道这个错误的确切原因。 @SAAD 感谢您的回复。我带来的不便表示歉意。当UrlFetchApp
被自定义函数和脚本编辑器使用时,我认为区别在于是否使用IPv6,而IPv4的地址每次运行都会改变。在这种情况下,脚本编辑器和自定义菜单的结果是相同的。我认为这可能是您的问题的原因。但我不确定我的猜测是否正确。这是因为我的技术不好。对此我深表歉意。
不错。但是ip4地址是谷歌的服务器地址,ip6地址是你的。不同之处在于额外的地址,这不是“origin”标头的正确语法。区别不是ip6 vs ip4。例如,如果 ip6 地址受到限制并且只允许 ip4,您将得到“###IP4 ### IP4###”。第一个ip4是你的。第二个是谷歌的。
@TheMaster 感谢您的评论。哦!对不起。我注意到我误解了 IPv6。我能理解你的评论。在这种情况下,似乎当有 2 个 origin
值时,就会出现问题。因为当我删除我的 IPV6 时,我确认 origin
的值是 2 个 IPV4 地址。我忘记了我同时使用 IPV4 和 IPV6。
Tbh,我不是这些 IP 差异方面的专家,但鉴于您提供的详细解释和测试,我会接受答案以上是关于UrlFetchApp 请求在菜单函数中失败,但在自定义函数中失败(连接到外部 REST API)的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Google Apps 脚本中向 UrlFetchApp 添加 API 密钥
UrlFetchApp Google Scripts VS Node Fetch
Google 应用程序脚本:urlfetchapp - 服务调用次数过多