我创建了一个替换Google Doc中令牌的Apps脚本。此App脚本已部署为API可执行文件。我可以在应用程序脚本编辑器中运行一个函数,没有任何授权错误。但是,当从我的java Web应用程序调用时,它有时会失败并出现Authorization错误。我收到以下错误:
{
"name": "replaceTokensInDoc",
"done": true,
"error": {
"code": 3,
"message": "ScriptError",
"details": [
{
"@type": "type.googleapis.com/google.apps.script.v1.ExecutionError",
"errorMessage": "Authorization is required to perform that action.",
"errorType": "ScriptError"
}
]
}
}
我已在多个地方阅读过我需要在脚本编辑器中运行一个函数并提供解决此问题的权限,但在我的案例中它没有帮助。当我在编辑器中运行一个函数时,它甚至不会显示授权对话框,这意味着它具有所有必要的权限。 有时只会失败。有人能让我知道这种奇怪行为的原因吗?
答案 0 :(得分:4)
出现此错误的原因是,当访问令牌expires_in
时间少于6分钟时,Google Apps脚本会返回授权错误。 Apps脚本执行所允许的最长时间为6分钟,因此它希望确保在执行Apps脚本时令牌不会过期。这是生产中的一个主要问题。这应该已在文档中以粗体发布。
Google问题跟踪器中已经存在问题。如果您遇到同样的问题,请为其加注星标。
https://issuetracker.google.com/issues/36762863
在问题解决之前,解决方法是在令牌过期少于360秒时刷新令牌。
if (credential.getExpiresInSeconds() <= 360) {
credential.refreshToken();
}
答案 1 :(得分:0)
execute.py示例(https://developers.google.com/apps-script/api/how-tos/execute#step_4_make_the_scriptrun_request)的python解决方案
基本上,如果到期时间少于361秒,则刷新令牌
from __future__ import print_function
from googleapiclient import errors
from googleapiclient.discovery import build
#from apiclient.discovery import build
from httplib2 import Http
from oauth2client import file as oauth_file, client, tools
import sys
import datetime
def main():
"""Runs the sample.
"""
SCRIPT_ID = 'YOUR SCRIPT ID'
# Setup the Apps Script API
SCOPES = [
'https://www.googleapis.com/auth/script.external_request'
,'https://www.googleapis.com/auth/spreadsheets'
,'https://www.googleapis.com/auth/drive'
,'https://www.googleapis.com/auth/drive.scripts'
,'https://www.googleapis.com/auth/userinfo.email'
]
store = oauth_file.Storage('token.json')
creds = store.get()
now = datetime.datetime.utcnow()
secondsDiff = (creds.token_expiry-now).total_seconds()
print(secondsDiff)
if secondsDiff < 361:
http = creds.authorize(Http())
creds.refresh(http)
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('script','v1',credentials=creds)
# Create an execution request object.
request = {"function": "doGet", "parameters": [{"sheetId" : sys.argv[1]}]}
try:
# Make the API request.
response = service.scripts().run(body=request,
scriptId=SCRIPT_ID).execute()
if 'error' in response:
# The API executed, but the script returned an error.
# Extract the first (and only) set of error details. The values of
# this object are the script's 'errorMessage' and 'errorType', and
# an list of stack trace elements.
error = response['error']['details'][0]
print("Script error message: {0}".format(error['errorMessage']))
if 'scriptStackTraceElements' in error:
# There may not be a stacktrace if the script didn't start
# executing.
print("Script error stacktrace:")
for trace in error['scriptStackTraceElements']:
print("\t{0}: {1}".format(trace['function'],
trace['lineNumber']))
#else:
# # The structure of the result depends upon what the Apps Script
# # function returns. Here, the function returns an Apps Script Object
# # with String keys and values, and so the result is treated as a
# # Python dictionary (folderSet).
# folderSet = response['response'].get('result', {})
# if not folderSet:
# print('No folders returned!')
# else:
# print('Folders under your root folder:')
# for (folderId, folder) in folderSet.iteritems():
# print("\t{0} ({1})".format(folder, folderId))
except errors.HttpError as e:
# The API encountered a problem before the script started executing.
print(e.content)
if __name__ == '__main__':
main()