运行 从 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可以设置为corsno-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=4URLSearchParams 可用于从 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));
    }