首页 > 解决方案 > 如何在 Web 界面中授权应用脚本?

问题描述

我有一个简单的应用程序脚本,它在驱动器中搜索名为“某物”的文件夹,并在该文件夹不存在时列出其中的文件名,它会创建它。

function doGet() {
 var folders = DriveApp.getFoldersByName('something');
 if (folders.hasNext()) {
  var files = folders.next().getFiles();
    var fileNames  = [];
    while (files.hasNext()) {
      var file = files.next();
      fileNames.push(file.getName())
    };
    return ContentService.createTextOutput(JSON.stringify({
        'status': 'success',
        'output': fileNames
    }))

  } else {
    DriveApp.createFolder('something');
    return ContentService.createTextOutput(JSON.stringify({
        'status': 'success',
        'output': 'folder created'
    }))
  }
}

我将脚本部署为 Web 应用程序,以我自己的身份执行该应用程序,使用来自浏览器中的 react 应用程序的 fetch 调用对其进行测试,一切都按预期工作。

然后我以 Web 应用程序的形式重新发布,以访问 Web 应用程序的用户身份执行。这次没有任何效果。我在浏览器中收到以下错误

访问从源“ http://localhost:3000 ”获取的“ https://script.google.com/macros/s/AKfycbx4eQ60DziUy4hSjnJidW9WVwBsT_qruQHa_BrK508T4oD9ILY/exec ”已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。

我预计会出现错误,因为没有身份验证。我在 react 应用程序上设置了 google OAuth2,添加了脚本和驱动范围,在 google 开发人员控制台中注册了该应用程序并设置了所有内容,以便accessToken我可以通过对 google 脚本的 fetch 调用将其发送到标题中。

但我仍然得到与上述相同的错误。

我应该如何授权应用程序脚本应用程序以便任何人都可以运行它?

我正在尝试创建一个带有google登录按钮的网络应用程序,当用户登录时调用appscript我部署的并在他们的驱动器中搜索文件夹名称“某物”,以便我可以在我的网络应用程序中显示文件的名称。

这不是 cors 问题,如果我以自己的身份执行 web 应用程序,则没有 cors 问题并且对代码的更改为零。我的问题是关于我如何授权应用脚本?我只提到了 cors 错误,因为我展示了我到目前为止所尝试的内容。

标签: javascriptgoogle-apps-scriptweb-applicationsoauth-2.0cors

解决方案


如果您的调用中的任何函数引发异常,您将收到 CORS 错误,因为您没有try...catch处理程序。当部署的 webapp未能返回有效响应(即 anHtmlOutputTextOutput时,Google 的错误页面不包含任何 CORS 标头。

为防止这些“屏蔽”CORS 错误,请将您的 Web 应用程序入口点包装起来,try...catch以便报告实际错误。

即使不使用 包装您的 web 应用程序的入口点try...catch,您也很可能能够通过查看项目的 Stackdriver 日志/Stackdriver 错误报告来访问底层错误。

这是一个简单的示例,演示了一些事件对象验证,以确保请求来自您关心的某个地方并且格式正确,然后调用一个可能为无效/未经授权的操作抛出异常的函数。无论哪种情况,它都会返回一个不透明的面向用户的响应(“对不起!”),并且对于错误将生成一个 Stackdriver 日志。它无法捕获因执行时间配额违规而发生的异常,因为您的脚本只是被杀死了。

function doGet(e) {
  if (!e || !validate_(e)) {
    return fail_();
  }
  try {
    return walkDriveContent(...);
  }
  catch (err) {
    console.error({message: "Drive walk failed: " + err.message, stack: err.stack, error: err});
    return fail_();
  }
}

function fail_() {
  return ContentService.createTextOutput("Sorry!");
}

function validate_(eventObj) {
 /** your code here that ensures this is a valid event object you can work with */
 // return true;
 //     or
 // return false;
}

function walkDriveContent(folderName) {
  /**
   * Your code that may throw exceptions here, but otherwise
   * returns TextOutput or HtmlOutput
   */
}

推荐阅读