403错误消息" Google幻灯片API尚未在项目中使用...之前或已被禁用"

时间:2016-12-28 18:01:22

标签: google-apps-script google-apps google-slides-api

我正在尝试从Google表格生成Google幻灯片;已经使用Sheets脚本没有问题,但是当我尝试包含Google幻灯片时,在进行身份验证并获得Oauth权限提示后,我收到此错误,我找不到任何参考;我确保在开发人员控制台中启用了Google幻灯片API和云端硬盘API。

" https://slides.googleapis.com/v1/presentations/请求失败...返回代码403.截断的服务器响应:{"错误":{"代码":403,&# 34;消息":"谷歌幻灯片API尚未在项目项目ID -...中使用之前或它是disab ...(使用muteHttpExceptions选项检查完整响应)(第93行,文件& #34;代码&#34)"

代码失败如下,失败的功能是从How to download Google Slides as images?复制的。定义客户端ID和机密,仅为安全性而忽略

// from https://mashe.hawksey.info/2015/10/setting-up-oauth2-access-with-google-apps-script-blogger-api-example/

function getService() {
  // Create a new service with the given name. The name will be used when
  // persisting the authorized token, so ensure it is unique within the
  // scope of the property store.
  return OAuth2.createService('slidesOauth')

      // Set the endpoint URLs, which are the same for all Google services.
      .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')


      // Set the client ID and secret, from the Google Developers Console.
      .setClientId(CLIENT_ID)
      .setClientSecret(CLIENT_SECRET)

      // Set the name of the callback function in the script referenced
      // above that should be invoked to complete the OAuth flow.
      .setCallbackFunction('authCallback')

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getUserProperties())

      // Set the scopes to request (space-separated for Google services).
      // this is blogger read only scope for write access is:
      // https://www.googleapis.com/auth/blogger
      .setScope('https://www.googleapis.com/auth/blogger.readonly')

      // Below are Google-specific OAuth2 parameters.

      // Sets the login hint, which will prevent the account chooser screen
      // from being shown to users logged in with multiple accounts.
      .setParam('login_hint', Session.getActiveUser().getEmail())

      // Requests offline access.
      .setParam('access_type', 'offline')

      // Forces the approval prompt every time. This is useful for testing,
      // but not desirable in a production application.
      .setParam('approval_prompt', 'force');
}

function authCallback(request) {
  var oauthService = getService();
  var isAuthorized = oauthService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput('Denied. You can close this tab');
  }
}

// from https://stackoverflow.com/questions/31662455/how-to-download-google-slides-as-images/40678925#40678925

function downloadPresentation(id) {
  var slideIds = getSlideIds(id); 

  for (var i = 0, slideId; slideId = slideIds[i]; i++) {
    downloadSlide('Slide ' + (i + 1), id, slideId);
  }
}
function downloadSlide(name, presentationId, slideId) {
  var url = 'https://docs.google.com/presentation/d/' + presentationId +
    '/export/png?id=' + presentationId + '&pageid=' + slideId; 
  var options = {
    headers: {
      Authorization: 'Bearer ' + getService().getAccessToken()
    }
  };
  var response = UrlFetchApp.fetch(url, options); // This is the failing line 93
  var image = response.getAs(MimeType.PNG);
  image.setName(name);
  DriveApp.createFile(image);
}

3 个答案:

答案 0 :(得分:1)

编辑: 我使用了这段代码:

var CLIENT_ID = '...';
var CLIENT_SECRET = '...';
var PRESENTATION_ID = '...';

// from https://mashe.hawksey.info/2015/10/setting-up-oauth2-access-with-google-apps-script-blogger-api-example/

function getService() {
  // Create a new service with the given name. The name will be used when
  // persisting the authorized token, so ensure it is unique within the
  // scope of the property store.
  return OAuth2.createService('slidesOauth')

      // Set the endpoint URLs, which are the same for all Google services.
      .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')


      // Set the client ID and secret, from the Google Developers Console.
      .setClientId(CLIENT_ID)
      .setClientSecret(CLIENT_SECRET)

      // Set the name of the callback function in the script referenced
      // above that should be invoked to complete the OAuth flow.
      .setCallbackFunction('authCallback')

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getUserProperties())

      // Set the scopes to request (space-separated for Google services).
      .setScope('https://www.googleapis.com/auth/drive')

      // Below are Google-specific OAuth2 parameters.

      // Sets the login hint, which will prevent the account chooser screen
      // from being shown to users logged in with multiple accounts.
      .setParam('login_hint', Session.getActiveUser().getEmail())

      // Requests offline access.
      .setParam('access_type', 'offline')

      // Forces the approval prompt every time. This is useful for testing,
      // but not desirable in a production application.
      .setParam('approval_prompt', 'force');
}

function authCallback(request) {
  var oauthService = getService();
  var isAuthorized = oauthService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput('Denied. You can close this tab');
  }
}

function getSlideIds(presentationId) {
  var url = 'https://slides.googleapis.com/v1/presentations/' + presentationId;
  var options = {
    headers: {
      Authorization: 'Bearer ' + getService().getAccessToken()
    }
  };
  var response = UrlFetchApp.fetch(url, options);

  var slideData = JSON.parse(response);
  return slideData.slides.map(function(slide) {
    return slide.objectId;
  });
}


// from http://stackoverflow.com/questions/31662455/how-to-download-google-slides-as-images/40678925#40678925

function downloadPresentation(id) {
  var slideIds = getSlideIds(id); 

  for (var i = 0, slideId; slideId = slideIds[i]; i++) {
    downloadSlide('Slide ' + (i + 1), id, slideId);
  }
}

function downloadSlide(name, presentationId, slideId) {
  var url = 'https://docs.google.com/presentation/d/' + presentationId +
    '/export/png?id=' + presentationId + '&pageid=' + slideId; 
  var options = {
    headers: {
      Authorization: 'Bearer ' + getService().getAccessToken()
    }
  };
  var response = UrlFetchApp.fetch(url, options); // This is the failing line 93
  var image = response.getAs(MimeType.PNG);
  image.setName(name);
  DriveApp.createFile(image);
}

function start() {
  var service = getService();
  var authorizationUrl = service.getAuthorizationUrl();
  Logger.log('Open the following URL and re-run the script: %s',
      authorizationUrl);

  if (service.hasAccess()) {
    downloadPresentation(PRESENTATION_ID);
  }
}

我猜客户ID和秘密并非来自您认为来自的项目。您可以访问your project's credentials page并查看“OAuth 2.0客户端ID'”下是否列出了匹配的客户端ID,以验证这一点。包含该客户端ID的项目需要启用Slides API。

另请注意:您使用的/ export / png端点不是文档/支持的Google API,因此将来可能会重命名或中断。如果您对通过幻灯片API获取幻灯片的PNG的官方API感兴趣,请按照此issue on the tracker进行操作。

上一页内容:

您的代码与您要复制的代码段略有不同。它使用ScriptApp.getOAuthToken()获取授权标头的值,但您正在调用其他getService().getAccessToken()功能。您似乎正在使用apps-script-oauth2库来生成OAuth令牌。如果是这种情况,请确认在生成clientId的开发人员控制台项目上启用了Slides API,并将您传递给OAuth2.createService的客户端密码,因为它不一定是附加的同一个项目到你的脚本。如果切换到ScriptApp.getOAuthToken()是您的选择,那么也可以。

如果这不能解决您的问题,请介意提供更多代码?您粘贴的代码段似乎与错误消息不符,因为您的代码似乎向docs.google.com发出了请求,而不是错误中提到的slides.googleapis.com。

答案 1 :(得分:0)

解决方案的简短版本:感谢Maurice Codik的努力,我得到了他的代码和我的工作。

问题在于OAuth凭据中的授权重定向URI设置,必须将其设置为

https://script.google.com/macros/d/[ScriptID]/usercallback

答案 2 :(得分:-1)

这不是对OP问题的直接回答,而是直接解决了他们第一句的第一部分,即"我正在尝试从Google表格中生成Google幻灯片...."这是我为video (and accompanying blog post [s]创建的确切用例。注意:帖子中的有效负载是JSON,但视频中的完整示例是Python,因此非Python开发人员可以简单地将其用作伪代码。)