运行 从 Google 站点在 Webapp 中发布的函数
Run a function published in Webapp from a Google site
我想 运行 来自 google 站点的网络应用程序中的一个函数作为 onload
函数。
code.gs
function doGet(e){
return HtmlService.createHtmlOutputFromFile("page");
}
function myfunc(datavalues) {
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEET-ID-IS-HERE';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
activeSheet.getRange(1,1).setValue(datavalues[0]);
activeSheet.getRange(1,2).setValue(datavalues[1]);
// ---------------------------------------------------- //
}
page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<body>
</body>
<script>
// function run from code.gs
function runfunc(values){
google.script.run.myfunc(values);
}
</script>
</html>
最后,我在 google 站点中嵌入了以下代码:
<!DOCTYPE html>
<html>
<script src="https://script.google.com/a/macros/s/WEBAPPURL/exec"></script>
<script>
function loadFunc(){
var values = ['aaaa',123];
runfunc(values);
}
</script>
<body onload='loadFunc()'>
<body/>
</html>
这应该将值写入 google sheet,但它不起作用。甚至可以 运行 从 webapp 中实现这样的功能吗? 运行 还是有其他选择?
问题更新:
code.gs
更新为 doPost
code.gs
function doGet(e){
console.log("get request");
return HtmlService.createHtmlOutputFromFile("page");
}
function myfunc(datavalues) {
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEET-ID-IS-HERE';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
activeSheet.getRange(1,1).setValue(datavalues[0]);
activeSheet.getRange(1,2).setValue(datavalues[1]);
// ---------------------------------------------------- //
}
// Post method
function doPost(datavalues){
console.log("post Request");
// check the parameters
if(typeof e !== 'undefined')
var datavalues = JSON.stringify(JSON.parse(e.parameter));
console.log(datavalues);
var sheetURL = 'https://docs.google.com/spreadsheets/d/URL/';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
// first empty row
var dataRow = activeSheet.getDataRange().getLastRow()+1;
// ---------------------------------------------------- //
// writing data to the sheet
// ---------------------------------------------------- //
activeSheet.getRange(dataRow,1).setValue(datavalues['a']);
activeSheet.getRange(dataRow,2).setValue(datavalues['b']);
// ---------------------------------------------------- //
return ContentService.createTextOutput(JSON.stringify(e));
}
嵌入在 google 站点的代码:
<!DOCTYPE html>
<html>
<script>
function postRequest(url, data) {
return fetch(url, {
method: 'POST',
credentials: 'omit';
headers: {'Accept': 'application/json',
'Content-Type': 'application/json'
}
body: JSON.stringify(data);
});
}
function onloadfunc(){
console.log('loadding');
var data = {'a':'xxx','b':'ppp'};
postRequest('https://script.google.com/a/macros/s/WEBAPP/exec', data).then(function(response) {
console.log("ok");
}).catch(function(error) {
console.log(error);
}
</script>
<body onload="onloadfunc()">
<body/>
</html>
更新 doPost
和发布请求的功能。但还是不行。
控制台错误:
Failed to load resource: the server responded with a status of 405 ()
Access to fetch at
'https://script.google.com/a/macros/s/WEBAPPURL/exec'
from origin 'https://2008039421-atari-embeds.googleusercontent.com'
has been blocked by CORS policy: Response to preflight request doesn't
pass access control check: No 'Access-Control-Allow-Origin' header is
present on the requested resource. If an opaque response serves your
needs, set the request's mode to 'no-cors' to fetch the resource with
CORS disabled.
我将 mode:'no-cors'
添加到 fetch
,现在 运行 是 onloadfunc()
,运行 是 fetch
。它给了我关于 fetch
或函数的其他错误,但现在它给了我以下错误。
Failed to load resource: the server responded with a status of 401 ()
网络应用程序开放供网络中的任何人使用,google 站点也通过网络使用它。所以它不应该有身份验证问题。我现在不知道怎么了。
JSON.stringify
的最新错误
<!DOCTYPE html>
<html>
<script>
function postRequest(url, data) {
return fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: {
'Accept': 'application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded'
},
credentials:'include',
body: data
});
}
function onloadfunc(){
console.log('loading');
var data = JSON.stringify({'a':'xxx','b':'ppp'});
postRequest('https://script.google.com/a/WEBAPP/exec', data).then(function(response) {
var div = document.getElementById('errorOutput');
div.innerHTML = "response"+response;
console.log("ok");
}).catch(function(error) {
var div = document.getElementById('errorOutput');
div.innerHTML = "error"+error;
console.log(error);});
}
</script>
<body onload="onloadfunc()">
<div id="errorOutput"></div>
</body>
</html>
code.gs
// Post method
function doPost(e){
//console.log(JSON.stringify(e,null, 2));
// check the parameters
if(typeof e !== 'undefined')
console.log(e);
var datavalues = e.parameter;
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEETURL/';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
// first empty row
var dataRow = activeSheet.getDataRange().getLastRow()+1;
// ---------------------------------------------------- //
// writing data to the sheet
// ---------------------------------------------------- //
activeSheet.getRange(dataRow,1).setValue(datavalues.a);
activeSheet.getRange(dataRow,2).setValue(datavalues.b);
// ---------------------------------------------------- //
return ContentService.createTextOutput(JSON.stringify(e));
}
有了这个, doPost
被调用并且它起作用了!!!
我现在唯一的问题是我的字典传错了。
在站点中它是 {'a':'xxx','b':'ppp'}
但传递的总参数看起来像(console.log(e)
的结果):
{parameter={{"a":"xxx","b":"ppp"}=}, contextPath=, contentLength=31, queryString=, parameters={{"a":"xxx","b":"ppp"}=[]}, postData=FileUpload}
以某种方式添加了额外的 =
,然后按原样传递字典。
您可以使用 jQuery 在启动时加载函数。它看起来像这样,将此代码添加到您的 html 文件中:
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
$(function() {
loadFunc();
});
function loadFunc(){
var values = ['aaaa',123];
google.script.run
.withSuccessHandler(function(data) {
console.log('Success')
})
.withFailureHandler(function(msg) {
console.log('Error : '+msg);
})
.myfunc(values);
}
</script>
然后您在网站上嵌入带 iframe 的页面,它将加载空白页面并执行该功能。
您可以指定较小的 iframe 大小,以便 iframe 使用较小的 space。
史蒂芬
目标:
- 运行 使用 Google 应用程序脚本在域中发布的 WebApp 中的特定功能
- 访问:域中的任何人
- 来自
- 在同一域中发布了新 Google 个站点。
问题:
- CORS: Google 应用程序脚本 Web 应用程序(以下简称 Gas-webapp)和 Google 站点中的用户代码(以下简称, Gs webapp) 运行 在不同来源的 iframe 中 -
https://[DIFFERENT_URL].googleusercontent.com
在 *google.com
. 中沙盒化
脚本流程:
- 使用
Fetch
api 到 POST
数据,从 Google 个站点到 web-app。
- 收到来自 Google 个站点的
POST
请求并执行函数。
必读:
备注:
Gas web-app 默认情况下向任何经过身份验证的 CORS 请求提供以下 CORS 响应 header:
Access-Control-Allow-Origin : *
但是,如果请求未经身份验证或脚本出错,您将被重定向到 Google 的登录 page/error 页面;两个页面都没有 Access-Control-Allow-Origin
设置,所有网页的访问都将被阻止。
上述未验证请求的唯一例外是发布 Gas web-app 访问权限:Anyone,
even anonymous
.
Gas web-app 不允许 OPTIONS
方法。因此,所有 preflighted 的请求都会失败。
与 gas-web 应用程序兼容的唯一剩余 CORS 选项是 Simple requests
,浏览器未对其进行预检。
但是,简单的 POST
请求基本上是未经身份验证的。在此脚本中,我们在请求中使用 credentials
option 来包含第三方凭据。在这里,我们需要用户帐户 Google 的凭据。通过使用此选项,我们实质上是使用用户帐户 Google 的凭据进行身份验证。必须满足以下条件才能使用此选项:
- 用户必须登录到 Google,以便使用 post 请求发送登录 cookie。
- 必须在浏览器中启用第三方 cookie,因为 iframe 来源不同。
request mode可以设置为cors
或no-cors
。
但是,如果设置了cors
,根据spec documentation,下面的响应header必须由gasweb-app提供给post 凭据请求:
Access-Control-Allow-Credentials
gas-web应用程序不提供此功能。虽然请求将被发送(因为它没有预检),但无法接收响应(未共享),即 doPost
仍将 运行,但 [= 无法接收响应169=].
如果设置了 no-cors
,fetch 将 return 一个 "opaque filtered response"(空响应)。
An opaque filtered response 是类型为“opaque”的过滤响应,URLlist为空列表,status为0,status message为空字节序列,headerlist为空,body 为空,预告片为空。
本质上,当访问设置为 Anyone
(非匿名)时,您可以进行从 gs-web-app 到 gas-web-app 的单向通信).
POST
使用 application/x-www-form-urlencoded
发出的请求必须是格式字符串:input1=1&input2=4
。 URLSearchParams
可用于从 objects.
制作此类字符串
片段:
<!DOCTYPE html>
<html>
<script>
function postRequest(url, data) {
return fetch(url, {
method: 'POST',
mode: 'no-cors',//or 'cors': Though request fails, doPost will execute
headers: {
'Accept': '*/*',//**Modified**
'Content-Type': 'application/x-www-form-urlencoded'
},
credentials:'include',//Access Credentials in browser cookies
body: data
});
}
function onloadfunc(){
console.log('loading');
var data = (new URLSearchParams({'a':'xxx','b':'ppp'})).toString();//**MODIFIED**
postRequest('https://script.google.com/a/WEBAPP/exec', data).then(function(response) {
//************OPAQUE RESPONSE: Nothing will be received************
var div = document.getElementById('errorOutput');
response.text(res=>
div.innerHTML = "response "+res)
console.log("ok");
}).catch(function(error) {
var div = document.getElementById('errorOutput');
div.innerHTML = "error"+error;
console.log(error);});
}
</script>
<body onload="onloadfunc()">
<div id="errorOutput"></div>
</body>
</html>
code.gs
// Post method
function doPost(e){
console.log(JSON.stringify(e,null, 2));
// check the parameters
if(typeof e !== 'undefined')
console.log(e);
var datavalues = e.parameter;
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEETURL/';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
// first empty row
var dataRow = activeSheet.getDataRange().getLastRow()+1;
// ---------------------------------------------------- //
// writing data to the sheet
// ---------------------------------------------------- //
activeSheet.getRange(dataRow,1).setValue(datavalues.a);
activeSheet.getRange(dataRow,2).setValue(datavalues.b);
//WON'T BE RECEIVED BY the post requester anyway//
return ContentService.createTextOutput(JSON.stringify(e));
}
我想 运行 来自 google 站点的网络应用程序中的一个函数作为 onload
函数。
code.gs
function doGet(e){
return HtmlService.createHtmlOutputFromFile("page");
}
function myfunc(datavalues) {
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEET-ID-IS-HERE';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
activeSheet.getRange(1,1).setValue(datavalues[0]);
activeSheet.getRange(1,2).setValue(datavalues[1]);
// ---------------------------------------------------- //
}
page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<body>
</body>
<script>
// function run from code.gs
function runfunc(values){
google.script.run.myfunc(values);
}
</script>
</html>
最后,我在 google 站点中嵌入了以下代码:
<!DOCTYPE html>
<html>
<script src="https://script.google.com/a/macros/s/WEBAPPURL/exec"></script>
<script>
function loadFunc(){
var values = ['aaaa',123];
runfunc(values);
}
</script>
<body onload='loadFunc()'>
<body/>
</html>
这应该将值写入 google sheet,但它不起作用。甚至可以 运行 从 webapp 中实现这样的功能吗? 运行 还是有其他选择?
问题更新:
code.gs
更新为 doPost
code.gs
function doGet(e){
console.log("get request");
return HtmlService.createHtmlOutputFromFile("page");
}
function myfunc(datavalues) {
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEET-ID-IS-HERE';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
activeSheet.getRange(1,1).setValue(datavalues[0]);
activeSheet.getRange(1,2).setValue(datavalues[1]);
// ---------------------------------------------------- //
}
// Post method
function doPost(datavalues){
console.log("post Request");
// check the parameters
if(typeof e !== 'undefined')
var datavalues = JSON.stringify(JSON.parse(e.parameter));
console.log(datavalues);
var sheetURL = 'https://docs.google.com/spreadsheets/d/URL/';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
// first empty row
var dataRow = activeSheet.getDataRange().getLastRow()+1;
// ---------------------------------------------------- //
// writing data to the sheet
// ---------------------------------------------------- //
activeSheet.getRange(dataRow,1).setValue(datavalues['a']);
activeSheet.getRange(dataRow,2).setValue(datavalues['b']);
// ---------------------------------------------------- //
return ContentService.createTextOutput(JSON.stringify(e));
}
嵌入在 google 站点的代码:
<!DOCTYPE html>
<html>
<script>
function postRequest(url, data) {
return fetch(url, {
method: 'POST',
credentials: 'omit';
headers: {'Accept': 'application/json',
'Content-Type': 'application/json'
}
body: JSON.stringify(data);
});
}
function onloadfunc(){
console.log('loadding');
var data = {'a':'xxx','b':'ppp'};
postRequest('https://script.google.com/a/macros/s/WEBAPP/exec', data).then(function(response) {
console.log("ok");
}).catch(function(error) {
console.log(error);
}
</script>
<body onload="onloadfunc()">
<body/>
</html>
更新 doPost
和发布请求的功能。但还是不行。
控制台错误:
Failed to load resource: the server responded with a status of 405 () Access to fetch at 'https://script.google.com/a/macros/s/WEBAPPURL/exec' from origin 'https://2008039421-atari-embeds.googleusercontent.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
我将 mode:'no-cors'
添加到 fetch
,现在 运行 是 onloadfunc()
,运行 是 fetch
。它给了我关于 fetch
或函数的其他错误,但现在它给了我以下错误。
Failed to load resource: the server responded with a status of 401 ()
网络应用程序开放供网络中的任何人使用,google 站点也通过网络使用它。所以它不应该有身份验证问题。我现在不知道怎么了。
JSON.stringify
<!DOCTYPE html>
<html>
<script>
function postRequest(url, data) {
return fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: {
'Accept': 'application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded'
},
credentials:'include',
body: data
});
}
function onloadfunc(){
console.log('loading');
var data = JSON.stringify({'a':'xxx','b':'ppp'});
postRequest('https://script.google.com/a/WEBAPP/exec', data).then(function(response) {
var div = document.getElementById('errorOutput');
div.innerHTML = "response"+response;
console.log("ok");
}).catch(function(error) {
var div = document.getElementById('errorOutput');
div.innerHTML = "error"+error;
console.log(error);});
}
</script>
<body onload="onloadfunc()">
<div id="errorOutput"></div>
</body>
</html>
code.gs
// Post method
function doPost(e){
//console.log(JSON.stringify(e,null, 2));
// check the parameters
if(typeof e !== 'undefined')
console.log(e);
var datavalues = e.parameter;
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEETURL/';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
// first empty row
var dataRow = activeSheet.getDataRange().getLastRow()+1;
// ---------------------------------------------------- //
// writing data to the sheet
// ---------------------------------------------------- //
activeSheet.getRange(dataRow,1).setValue(datavalues.a);
activeSheet.getRange(dataRow,2).setValue(datavalues.b);
// ---------------------------------------------------- //
return ContentService.createTextOutput(JSON.stringify(e));
}
有了这个, doPost
被调用并且它起作用了!!!
我现在唯一的问题是我的字典传错了。
在站点中它是 {'a':'xxx','b':'ppp'}
但传递的总参数看起来像(console.log(e)
的结果):
{parameter={{"a":"xxx","b":"ppp"}=}, contextPath=, contentLength=31, queryString=, parameters={{"a":"xxx","b":"ppp"}=[]}, postData=FileUpload}
以某种方式添加了额外的 =
,然后按原样传递字典。
您可以使用 jQuery 在启动时加载函数。它看起来像这样,将此代码添加到您的 html 文件中:
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
$(function() {
loadFunc();
});
function loadFunc(){
var values = ['aaaa',123];
google.script.run
.withSuccessHandler(function(data) {
console.log('Success')
})
.withFailureHandler(function(msg) {
console.log('Error : '+msg);
})
.myfunc(values);
}
</script>
然后您在网站上嵌入带 iframe 的页面,它将加载空白页面并执行该功能。 您可以指定较小的 iframe 大小,以便 iframe 使用较小的 space。
史蒂芬
目标:
- 运行 使用 Google 应用程序脚本在域中发布的 WebApp 中的特定功能
- 访问:域中的任何人
- 来自
- 在同一域中发布了新 Google 个站点。
问题:
- CORS: Google 应用程序脚本 Web 应用程序(以下简称 Gas-webapp)和 Google 站点中的用户代码(以下简称, Gs webapp) 运行 在不同来源的 iframe 中 -
https://[DIFFERENT_URL].googleusercontent.com
在*google.com
. 中沙盒化
脚本流程:
- 使用
Fetch
api 到POST
数据,从 Google 个站点到 web-app。 - 收到来自 Google 个站点的
POST
请求并执行函数。
必读:
备注:
Gas web-app 默认情况下向任何经过身份验证的 CORS 请求提供以下 CORS 响应 header:
Access-Control-Allow-Origin : *
但是,如果请求未经身份验证或脚本出错,您将被重定向到 Google 的登录 page/error 页面;两个页面都没有
Access-Control-Allow-Origin
设置,所有网页的访问都将被阻止。上述未验证请求的唯一例外是发布 Gas web-app 访问权限:
Anyone,
even anonymous
.Gas web-app 不允许
OPTIONS
方法。因此,所有 preflighted 的请求都会失败。与 gas-web 应用程序兼容的唯一剩余 CORS 选项是
Simple requests
,浏览器未对其进行预检。但是,简单的
POST
请求基本上是未经身份验证的。在此脚本中,我们在请求中使用credentials
option 来包含第三方凭据。在这里,我们需要用户帐户 Google 的凭据。通过使用此选项,我们实质上是使用用户帐户 Google 的凭据进行身份验证。必须满足以下条件才能使用此选项:- 用户必须登录到 Google,以便使用 post 请求发送登录 cookie。
- 必须在浏览器中启用第三方 cookie,因为 iframe 来源不同。
request mode可以设置为
cors
或no-cors
。但是,如果设置了
cors
,根据spec documentation,下面的响应header必须由gasweb-app提供给post 凭据请求:Access-Control-Allow-Credentials
gas-web应用程序不提供此功能。虽然请求将被发送(因为它没有预检),但无法接收响应(未共享),即 doPost
仍将 运行,但 [= 无法接收响应169=].
如果设置了
no-cors
,fetch 将 return 一个 "opaque filtered response"(空响应)。An opaque filtered response 是类型为“opaque”的过滤响应,URLlist为空列表,status为0,status message为空字节序列,headerlist为空,body 为空,预告片为空。
本质上,当访问设置为
Anyone
(非匿名)时,您可以进行从 gs-web-app 到 gas-web-app 的单向通信).
制作此类字符串POST
使用application/x-www-form-urlencoded
发出的请求必须是格式字符串:input1=1&input2=4
。URLSearchParams
可用于从 objects.
片段:
<!DOCTYPE html>
<html>
<script>
function postRequest(url, data) {
return fetch(url, {
method: 'POST',
mode: 'no-cors',//or 'cors': Though request fails, doPost will execute
headers: {
'Accept': '*/*',//**Modified**
'Content-Type': 'application/x-www-form-urlencoded'
},
credentials:'include',//Access Credentials in browser cookies
body: data
});
}
function onloadfunc(){
console.log('loading');
var data = (new URLSearchParams({'a':'xxx','b':'ppp'})).toString();//**MODIFIED**
postRequest('https://script.google.com/a/WEBAPP/exec', data).then(function(response) {
//************OPAQUE RESPONSE: Nothing will be received************
var div = document.getElementById('errorOutput');
response.text(res=>
div.innerHTML = "response "+res)
console.log("ok");
}).catch(function(error) {
var div = document.getElementById('errorOutput');
div.innerHTML = "error"+error;
console.log(error);});
}
</script>
<body onload="onloadfunc()">
<div id="errorOutput"></div>
</body>
</html>
code.gs
// Post method
function doPost(e){
console.log(JSON.stringify(e,null, 2));
// check the parameters
if(typeof e !== 'undefined')
console.log(e);
var datavalues = e.parameter;
var sheetURL = 'https://docs.google.com/spreadsheets/d/SHEETURL/';
// Current Active Sheet
var activeGoogleSheet = SpreadsheetApp.openByUrl(sheetURL);
var activeSheet = activeGoogleSheet.getActiveSheet();
// first empty row
var dataRow = activeSheet.getDataRange().getLastRow()+1;
// ---------------------------------------------------- //
// writing data to the sheet
// ---------------------------------------------------- //
activeSheet.getRange(dataRow,1).setValue(datavalues.a);
activeSheet.getRange(dataRow,2).setValue(datavalues.b);
//WON'T BE RECEIVED BY the post requester anyway//
return ContentService.createTextOutput(JSON.stringify(e));
}