【问题标题】:Chrome App using Apps Script API使用 Apps Script API 的 Chrome 应用
【发布时间】:2016-08-17 02:18:54
【问题描述】:

今天遇到了另一个障碍,在我编写用于记录用户项目时间的桌面 chrome 应用程序的道路上。

我想要做的(但失败了)是使用 Apps Script API 来访问一个谷歌表格,该表格保留了我想要在我的 Chrome 应用 UI 中填充下拉列表的信息(项目编号)。

更新: 我已经改写了这一点,以便直截了当并清楚我的问题是什么。

我似乎无法实现的是从我的 chrome 应用程序调用 Apps 脚本函数。我读过这个Execution API,但似乎仍然无法着色。由于某些原因,我在控制台中不断收到“未捕获的 ReferenceError:gapi 未定义”。

我设法做的是在开发者控制台中将 Apps 脚本和 Chrome 应用程序放在同一个项目名称下。不确定它是否需要,但认为它可能只对 1 个 Oauth2 请求有帮助。

我的脑袋是不是少了点什么?

任何帮助或想法将不胜感激。

这是我的 manifest.json

{
  "manifest_version": 2,
  "name": "TimeSheet",
  "description": "Small and easy desktop app for entering time spent on project files",
  "version": "0.1.0",
  "icons": {
    "128": "icon_128.png"
  },
  "app": {
    "background": {
      "scripts": ["background.js"]
    }
  },
  "permissions": [
    "identity",
    "app.window.alwaysOnTop"
    ],
  "oauth2": {
    "client_id": "clientid.apps.googleusercontent.com",
    "scopes": [
      "https://www.googleapis.com/auth/drive",
      "https://www.googleapis.com/auth/spreadsheets"
      
      ]
  },
  "key": "very long string"
}

这是在我的 main.js 中运行的 Oauth2 代码

//This code confirms Oauth2 for access to google drive and related files

window.onload = function(){
  
  document.querySelector("#Oauth2").addEventListener("click", function(){
    
    chrome.identity.getAuthToken({"interactive": true}, function(token){
      console.log(token);
      
    });
  });

};

// ID of the script to call. Acquire this from the Apps Script editor,
// under Publish > Deploy as API executable.
var scriptId = "blah";
// Create execution request.
var request = {
    'function': 'getProjectNumbers',
};

// Make the request.
var op = gapi.client.request({
    'root': 'https://script.googleapis.com',
    'path': 'v1/scripts/' + scriptId + ':run',
    'method': 'POST',
    'body': request
});
// Log the results of the request.
op.execute(function(resp) {
  if (resp.error && resp.error.status) {
    // The API encountered a problem before the script started executing.
    console.log('Error calling API: ' + JSON.stringify(resp, null, 2));
  } else if (resp.error) {
    // The API executed, but the script returned an error.
    var error = resp.error.details[0];
    console.log('Script error! Message: ' + error.errorMessage);
    if (error.scriptStackTraceElements) {
      // There may not be a stacktrace if the script didn't start executing.
      console.log('Script error stacktrace:');
      for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
        var trace = error.scriptStackTraceElements[i];
        console.log('\t' + trace.function + ':' + trace.lineNumber);
      }
    }
  } else {
    // Here, the function returns an array of strings.
    var projectNumbers = resp.response.result;
    console.log('Project numbers in spreadsheet:');
    projectNumbers.forEach(function(name){
      console.log(name);
    });
  }
});

这是应用程序脚本代码:

var projectDatabaseKey = 'blah'; //Project Database Sheet spreadsheet key
var pprojectDatabaseSheet = 'Project Database'; //Project Database  Sheet spreadsheet sheet

//Function to revieve data of project numbers for drop down list
function getProjectNumbers() {
  return SpreadsheetApp
   .openById(projectDatabaseKey).getSheetByName(projectDatabaseSheet)
   .getRange("A2:A" + (SpreadsheetApp.openById(projectDatabaseKey).getSheetByName(projectDatabaseSheet).getLastRow()))
   .getValues();
}

我真的不确定如何使用 Oauth2 令牌以及如何将其应用于应用程序脚本。

更新

好的,我尝试在不同的庄园中调用应用程序脚本,我今天尝试的是使用 gapi-chrome-apps.js 库来完成 oauth2 工作。

现在我的问题是我收到了这个错误,这可能是我猜测的一系列问题:

发布https://www.googleapis.com/v1/scripts/blahblah:run404 ()

gapi.client.request@VM80 gapi-chrome-apps.js:105

getSheetsList@gapiCallback.js:17

(匿名函数)@gapiCallback.js:49

callbackWrapper @ VM80 gapi-chrome-apps.js:68

target.(匿名函数)@extensions::SafeBuiltins:19

safeCallbackApply @extensions::sendRequest:21

handleResponse @ extensions::sendRequest:72

这个错误来自 gapi-chrome-apps.js 脚本:

未捕获的 SyntaxError:JSON 中位置 0 处的意外标记 N

真的不知道是什么原因造成的,这是我更新的代码:

//get listof sheets in spreadsheet
function getSheetsList(){
  var scriptId = "blahblah";
  // Initialize parameters for function call.
  var sheetId = "blahblah";
  // Create execution request.
  var requests = {
      'function': 'getSheetNames',
      'parameters': [sheetId],
      'devMode': true   // Optional.
  };
  // Make the request.
  gapi.client.request({
      'root': 'https://script.googleapis.com',
      'path': 'v1/scripts/' + scriptId + ':run',
      'method': 'POST',
      'body': requests,
      'callback': printSheetsList
  });
}
// Log the results of the request.
function printSheetsList(resp) {
  if (resp.error && resp.error.status) {
    // The API encountered a problem before the script started executing.
    console.log('Error calling API: ' + JSON.stringify(resp, null, 2));
  } else if (resp.error) {
    // The API executed, but the script returned an error.
    var error = resp.error.details[0];
    console.log('Script error! Message: ' + error.errorMessage);
  } else {
    // Here, the function returns an array of strings.
    var sheetNames = resp.response.result;
    console.log('Sheet names in spreadsheet:');
    sheetNames.forEach(function(name){
    console.log(name);
    });
  }
}
//Prompts the user for authorization and then proceeds to 
function authorize(params, callback) {
  gapi.auth.authorize(params, function(accessToken) {
    if (!accessToken) {
      console.log("Error getting authorization");
    } else {
      callback();
    }
  });
}
function gapiIsLoaded() {
  var params = { 'immediate': true };
  if (!(chrome && chrome.app && chrome.app.runtime)) {
    params.scope = "https://www.googleapis.com/auth/drive";
    params.client_id = "blahblah";
    gapi.auth.init(authorize.bind(null, params, getSheetsList));
  } else {
    authorize(params, getSheetsList);
  }
}

【问题讨论】:

  • 欢迎来到stackoverflow。请特别参阅how to write a good stackoverflow question,向我们展示您尝试调用应用程序脚本执行 API 以及失败的地方。
  • 我已经在请求中添加了我正在制作应用程序脚本但不确定如何与 Oauth2 建立连接,我也得到“未捕获的 ReferenceError:gapi 未定义”我猜这是原因我错过了一个可能与 Oauth2 和 Apps 脚本之间的连接的步骤。我是这些 chrome 应用程序的一个相当大的菜鸟。比我习惯的应用程序脚本要多得多。
  • 我尝试了一些不同的方法,看起来我的问题是 .gapi 并且由于安全策略而无法加载客户端库 api。将不得不尝试解决方法。
  • 我看不到您在哪里添加访问令牌。您是否按照调用执行 api 的示例进行操作?为什么不使用客户端库? developers.google.com/apps-script/guides/rest/…
  • 尝试从网页加载库并查看它最终下载的 js 文件。您需要将该文件本地包含在扩展中,或者像您一样使用访问令牌进行正确的 POST 调用。查看其他使用 post/get 进行身份验证呼叫主管的谷歌 API,并模仿身份验证部分(承载等)

标签: javascript google-apps-script google-chrome-app


【解决方案1】:

使用传统的 GAPI 将不起作用,因为它会动态加载更多外部脚本和应用程序are not allowed to do that

一种可能的解决方案是在sandboxed page 中运行GAPI 代码,这样可以克服远程代码限制。但是,这很麻烦,因为您需要使用 postMessage 来回传递数据。

另一种方法是尝试使用 Google 提供的库 gapi-chrome-apps.js,它适用于 Chrome 应用程序(并使用 chrome.identity 管理 OAuth) - 但请注意此评论:

如果不进行额外修改,此库可能不适合使用。

【讨论】:

  • 嗨,Xan,我一直在尝试使用 gapi-chrome-apps.js,但您对所需的额外修改完全正确。它仍然需要几个回调函数,但更有意义,因为它需要范围和客户端 ID 我认为必须是请求过程的一部分。我将在周末尝试几种选择,希望能有所作为。出于某种原因,我认为因为它们都是谷歌服务,所以它们可以轻松地协同工作。我真是个傻瓜,哈哈
  • GAPI 旨在动态加载远程代码。应用程序旨在严格限制外部依赖项。你可以看到这艘火车残骸的去向。
  • 你认为有没有办法只使用身份 api?这对于获取应用程序自身的 oauth2 是一种享受,但我必须了解将其用于传递应用程序脚本 oauth2 之类的功能所涉及的内容。所有这些头撞的好处是我正在学习更多:)
【解决方案2】:

根据您的帖子,您根本没有定义gapi。可以这样加载

jQuery.getScript( "https://apis.google.com/js/api.js", onApiLoad );

其中onApiLoad 是您希望在加载gapi 时调用的函数。

对于您的代码,我会将以下代码包装在这样的函数中:

function onApiLoad() {
    // Make the request.
    var op = gapi.client.request({
        'root': 'https://script.googleapis.com',
        'path': 'v1/scripts/' + scriptId + ':run',
        'method': 'POST',
        'body': request
    });
    // Log the results of the request.
    op.execute(function(resp) {
      if (resp.error && resp.error.status) {
        // The API encountered a problem before the script started executing.
        console.log('Error calling API: ' + JSON.stringify(resp, null, 2));
      } else if (resp.error) {
        // The API executed, but the script returned an error.
        var error = resp.error.details[0];
        console.log('Script error! Message: ' + error.errorMessage);
        if (error.scriptStackTraceElements) {
          // There may not be a stacktrace if the script didn't start executing.
          console.log('Script error stacktrace:');
          for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
            var trace = error.scriptStackTraceElements[i];
            console.log('\t' + trace.function + ':' + trace.lineNumber);
          }
        }
      } else {
        // Here, the function returns an array of strings.
        var projectNumbers = resp.response.result;
        console.log('Project numbers in spreadsheet:');
        projectNumbers.forEach(function(name){
          console.log(name);
        });
      }
    });

}

编辑:没有 jQuery,只有纯 JS

感谢the second answer in this post,下面的代码是$.getScript() 的纯JS 实现。它包含一个回调,所以下面的代码 sn-p 应该可以工作,假设您将代码包装在如上所述的函数中。

function getScript(source, callback) {
    var script = document.createElement('script');
    var prior = document.getElementsByTagName('script')[0];
    script.async = 1;
    prior.parentNode.insertBefore(script, prior);

    script.onload = script.onreadystatechange = function( _, isAbort ) {
        if(isAbort || !script.readyState || /loaded|complete/.test(script.readyState) ) {
            script.onload = script.onreadystatechange = null;
            script = undefined;

            if(!isAbort) { if(callback) callback(); }
        }
    };

    script.src = source;
}


getScript("https://apis.google.com/js/api.js", onApiLoad);

【讨论】:

  • 嗨埃里克,谢谢你的回答,我现在似乎唯一的问题是加载 jQuery,“jQuery 未定义”从我收集的通过清单加载它“content_scripts”在打包中是不允许的应用 :(
  • @David 添加了使用纯JS的解决方案,希望对您的问题有所帮助
  • 感谢 Eric 的帮助,这绝对是一线希望。唯一的问题是 CSP 不允许外部源。 main.js:39 Refused to load the script 'https://apis.google.com/js/api.js' because it violates the following Content Security Policy directive: "default-src 'self' blob: filesystem: chrome-extension-resource:" 我在 github 上找到了一个 .js 文件,供 gapi 保存为本地副本,但是在引用时我得到了这个:gapiIsLoaded callback function must be defined prior to loading gapi-chrome-apps.js 来自 gapi 本地文件。尝试使用我的谷歌魔法自动取款机。
  • 我复制了 api 并将 api 加载为本地脚本 getScript("api.js?onload=handleClientLoad", onApiLoad); 现在我从 var op = gapi.client.request({ 行得到 Uncaught TypeError: Cannot read property 'request' of undefined。它似乎是一堵又一堵的砖墙。我还尝试将页面放入沙盒中,这给了我同样的错误,所以至少它在代码中已经走到了这一步,只需要它来执行完整的客户端请求。
  • 投反对票:Chrome 应用程序are not allowed to load external scripts,所以所有这些都没有用。
猜你喜欢
  • 1970-01-01
  • 2014-02-22
  • 1970-01-01
  • 2015-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多