Google Apps Script API 提供
scripts.run
方法
远程执行指定的 Apps 脚本函数。您可以使用此方法
(在调用应用中),以在您的某个脚本项目中运行函数
并接收响应。
要求
您必须满足以下要求,调用方应用才能使用
scripts.run
方法。您必须:
将脚本项目部署为 API 可执行文件。您可以 根据需要部署、取消部署和重新部署项目。
为执行作业提供适当范围的 OAuth 令牌。 此 OAuth 令牌必须涵盖所有范围 而不是仅限于被调用的函数所使用的 ID。请参阅方法参考中的授权范围完整列表。
确保脚本和调用应用的 OAuth2 客户共享一个共同的 Google Cloud 项目。 Cloud 项目必须是标准 Cloud 项目; 为 Apps 脚本项目创建的默认项目是不够的。您可以使用新的标准 Cloud 项目,也可以使用现有项目。
scripts.run
方法
scripts.run
方法需要密钥标识信息才能运行:
您可以选择将脚本配置为在开发模式下执行。
此模式使用最近保存的脚本项目版本执行
而不是最近部署的版本。为此,您可以将
devMode
布尔值,
请求正文
发送至 true
。只有脚本的所有者才能在开发模式下执行脚本。
处理参数数据类型
使用 Apps Script API
scripts.run
方法
通常涉及将数据作为函数参数发送到 Apps 脚本,
以函数返回值的形式返回数据。该 API 只能接受和返回
基本类型的值:字符串、数组、对象、数字和布尔值。这些
类似于 JavaScript 中的基本类型。更复杂的 Apps 脚本对象(例如 Document 或 Sheet)无法通过 API 传入或传出脚本项目。
当您调用应用以强类型语言(例如
Java 中,它会以泛型对象列表或数组的形式传入参数
与这些基本类型相对应。在许多情况下,您可以将简单的
类型转换。例如,一个接受数字的函数
参数可以作为 Java Double
、Integer
或 Long
对象作为
参数,而无需进行额外处理。
当 API 返回函数响应时,您通常需要将 使其返回正确的类型,然后才能使用。以下是一些 基于 Java 的示例:
- API 返回给 Java 应用的编号以
java.math.BigDecimal
对象,可能需要将其转换为 根据需要使用Doubles
或int
类型。 如果 Apps 脚本函数返回字符串数组,则 Java 应用 将响应转换为
List<String>
对象:List<String> mylist = (List<String>)(op.getResponse().get("result"));
如果您希望返回
Bytes
的数组,可能会发现很方便 在 Apps 脚本函数中将数组编码为 base64 字符串; 返回该字符串:return Utilities.base64Encode(myByteArray); // returns a String.
下面的示例代码示例说明了 来解读 API 响应
一般程序
下面介绍了使用 Apps Script API 的一般流程 以执行 Apps 脚本函数:
第 1 步:设置通用 Cloud 项目
您的脚本和调用应用需要共用 Cloud 项目中。此 Cloud 项目可以是现有项目, 为此目的创建一个新项目。有了 Cloud 项目后 必须切换脚本项目才能使用它。
第 2 步:将脚本部署为 API 可执行文件
- 打开包含您要使用的函数的 Apps 脚本项目。
- 在右上角,依次点击部署 > 新建部署。
- 在打开的对话框中,点击“启用部署类型”图标
> API 可执行文件。
- 在“有权使用的人”下拉菜单中,选择 可以使用 Apps Script API 调用脚本的函数。
- 点击部署。
第 3 步:配置发起调用的应用
调用方应用必须启用 Apps Script API 并建立 OAuth 然后才能使用。您必须有权访问 Cloud 项目 来实现这一点
- 配置调用应用和脚本正在使用的 Cloud 项目。 您可以按照以下步骤执行此操作:
- 打开脚本项目,然后点击左侧的概览图标
。
- 在项目 OAuth 范围下,记录 脚本所需的资源。
在调用应用代码中,生成脚本 OAuth 访问令牌 。这并不是 API 本身使用的令牌,而是 所需的资源。应使用 Cloud 项目客户端 ID 和您记录的脚本范围。
Google 客户端库 帮助构建此令牌和处理应用的 OAuth, 通常可以让您构建更高级别的“凭据”对象 使用脚本范围进行控制请参阅 如需查看示例,请参阅 Apps Script API 快速入门 从范围列表构建凭据对象的流程。
第 4 步:发出 script.run
请求
配置调用应用后,您可以
scripts.run
调用。每个 API
调用包括以下步骤:
- 构建 API 请求 脚本 ID、函数名称 参数。
- 使
scripts.run
调用并加入您在 标头(如果使用基本的POST
请求),也可以使用凭据对象 使用脚本范围构建的 - 等待脚本执行完毕。脚本最多 6 分钟的执行时间,因此您的应用应有此时间。
- 脚本函数在完成后可能会返回一个值, 如果该值是受支持的类型,该值将传回应用程序。
您可以参阅 script.run
API 调用示例
。
API 请求示例
以下示例展示了如何在 Google Cloud 控制台中
调用 Apps 脚本函数来输出
文件夹中。Apps 脚本项目的脚本 ID
包含已执行的函数时,必须指定
ENTER_YOUR_SCRIPT_ID_HERE
。这些示例依赖于
Google API 客户端库
语言。
目标脚本
此脚本中的函数使用 Drive API。
您必须在以下位置启用 Drive API: 托管该脚本的项目。
此外,调用应用必须发送 OAuth 凭据,其中包括 以下云端硬盘范围:
https://www.googleapis.com/auth/drive
这里的示例应用程序使用 Google 客户端库构建 使用此范围的 OAuth 凭据对象。
/**
* Return the set of folder names contained in the user's root folder as an
* object (with folder IDs as keys).
* @return {Object} A set of folder names keyed by folder ID.
*/
function getFoldersUnderRoot() {
const root = DriveApp.getRootFolder();
const folders = root.getFolders();
const folderSet = {};
while (folders.hasNext()) {
const folder = folders.next();
folderSet[folder.getId()] = folder.getName();
}
return folderSet;
}
Java
/**
* Create a HttpRequestInitializer from the given one, except set
* the HTTP read timeout to be longer than the default (to allow
* called scripts time to execute).
*
* @param {HttpRequestInitializer} requestInitializer the initializer
* to copy and adjust; typically a Credential object.
* @return an initializer with an extended read timeout.
*/
private static HttpRequestInitializer setHttpTimeout(
final HttpRequestInitializer requestInitializer) {
return new HttpRequestInitializer() {
@Override
public void initialize(HttpRequest httpRequest) throws IOException {
requestInitializer.initialize(httpRequest);
// This allows the API to call (and avoid timing out on)
// functions that take up to 6 minutes to complete (the maximum
// allowed script run time), plus a little overhead.
httpRequest.setReadTimeout(380000);
}
};
}
/**
* Build and return an authorized Script client service.
*
* @param {Credential} credential an authorized Credential object
* @return an authorized Script client service
*/
public static Script getScriptService() throws IOException {
Credential credential = authorize();
return new Script.Builder(
HTTP_TRANSPORT, JSON_FACTORY, setHttpTimeout(credential))
.setApplicationName(APPLICATION_NAME)
.build();
}
/**
* Interpret an error response returned by the API and return a String
* summary.
*
* @param {Operation} op the Operation returning an error response
* @return summary of error response, or null if Operation returned no
* error
*/
public static String getScriptError(Operation op) {
if (op.getError() == null) {
return null;
}
// Extract the first (and only) set of error details and cast as a Map.
// The values of this map are the script's 'errorMessage' and
// 'errorType', and an array of stack trace elements (which also need to
// be cast as Maps).
Map<String, Object> detail = op.getError().getDetails().get(0);
List<Map<String, Object>> stacktrace =
(List<Map<String, Object>>) detail.get("scriptStackTraceElements");
java.lang.StringBuilder sb =
new StringBuilder("\nScript error message: ");
sb.append(detail.get("errorMessage"));
sb.append("\nScript error type: ");
sb.append(detail.get("errorType"));
if (stacktrace != null) {
// There may not be a stacktrace if the script didn't start
// executing.
sb.append("\nScript error stacktrace:");
for (Map<String, Object> elem : stacktrace) {
sb.append("\n ");
sb.append(elem.get("function"));
sb.append(":");
sb.append(elem.get("lineNumber"));
}
}
sb.append("\n");
return sb.toString();
}
public static void main(String[] args) throws IOException {
// ID of the script to call. Acquire this from the Apps Script editor,
// under Publish > Deploy as API executable.
String scriptId = "ENTER_YOUR_SCRIPT_ID_HERE";
Script service = getScriptService();
// Create an execution request object.
ExecutionRequest request = new ExecutionRequest()
.setFunction("getFoldersUnderRoot");
try {
// Make the API request.
Operation op =
service.scripts().run(scriptId, request).execute();
// Print results of request.
if (op.getError() != null) {
// The API executed, but the script returned an error.
System.out.println(getScriptError(op));
} else {
// The result provided by the API needs to be cast into
// the correct type, based upon what types the Apps
// Script function returns. Here, the function returns
// an Apps Script Object with String keys and values,
// so must be cast into a Java Map (folderSet).
Map<String, String> folderSet =
(Map<String, String>) (op.getResponse().get("result"));
if (folderSet.size() == 0) {
System.out.println("No folders returned!");
} else {
System.out.println("Folders under your root folder:");
for (String id : folderSet.keySet()) {
System.out.printf(
"\t%s (%s)\n", folderSet.get(id), id);
}
}
}
} catch (GoogleJsonResponseException e) {
// The API encountered a problem before the script was called.
e.printStackTrace(System.out);
}
}
JavaScript
/**
* Load the API and make an API call. Display the results on the screen.
*/
function callScriptFunction() {
const scriptId = '<ENTER_YOUR_SCRIPT_ID_HERE>';
// Call the Apps Script API run method
// 'scriptId' is the URL parameter that states what script to run
// 'resource' describes the run request body (with the function name
// to execute)
try {
gapi.client.script.scripts.run({
'scriptId': scriptId,
'resource': {
'function': 'getFoldersUnderRoot',
},
}).then(function(resp) {
const result = resp.result;
if (result.error && result.error.status) {
// The API encountered a problem before the script
// started executing.
appendPre('Error calling API:');
appendPre(JSON.stringify(result, null, 2));
} else if (result.error) {
// 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 array of stack trace elements.
const error = result.error.details[0];
appendPre('Script error message: ' + error.errorMessage);
if (error.scriptStackTraceElements) {
// There may not be a stacktrace if the script didn't start
// executing.
appendPre('Script error stacktrace:');
for (let i = 0; i < error.scriptStackTraceElements.length; i++) {
const trace = error.scriptStackTraceElements[i];
appendPre('\t' + trace.function + ':' + trace.lineNumber);
}
}
} else {
// The structure of the result will depend 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 JavaScript object (folderSet).
const folderSet = result.response.result;
if (Object.keys(folderSet).length == 0) {
appendPre('No folders returned!');
} else {
appendPre('Folders under your root folder:');
Object.keys(folderSet).forEach(function(id) {
appendPre('\t' + folderSet[id] + ' (' + id + ')');
});
}
}
});
} catch (err) {
document.getElementById('content').innerText = err.message;
return;
}
}
Node.js
/**
* Call an Apps Script function to list the folders in the user's root Drive
* folder.
*
*/
async function callAppsScript() {
const scriptId = '1xGOh6wCm7hlIVSVPKm0y_dL-YqetspS5DEVmMzaxd_6AAvI-_u8DSgBT';
const {GoogleAuth} = require('google-auth-library');
const {google} = require('googleapis');
// Get credentials and build service
// TODO (developer) - Use appropriate auth mechanism for your app
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/drive',
});
const script = google.script({version: 'v1', auth});
try {
// Make the API request. The request object is included here as 'resource'.
const resp = await script.scripts.run({
auth: auth,
resource: {
function: 'getFoldersUnderRoot',
},
scriptId: scriptId,
});
if (resp.error) {
// 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 array
// of stack trace elements.
const error = resp.error.details[0];
console.log('Script error message: ' + error.errorMessage);
console.log('Script error stacktrace:');
if (error.scriptStackTraceElements) {
// There may not be a stacktrace if the script didn't start executing.
for (let i = 0; i < error.scriptStackTraceElements.length; i++) {
const trace = error.scriptStackTraceElements[i];
console.log('\t%s: %s', trace.function, trace.lineNumber);
}
}
} else {
// The structure of the result will depend 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
// Node.js object (folderSet).
const folderSet = resp.response.result;
if (Object.keys(folderSet).length == 0) {
console.log('No folders returned!');
} else {
console.log('Folders under your root folder:');
Object.keys(folderSet).forEach(function(id) {
console.log('\t%s (%s)', folderSet[id], id);
});
}
}
} catch (err) {
// TODO(developer) - Handle error
throw err;
}
}
Python
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
def main():
"""Runs the sample."""
# pylint: disable=maybe-no-member
script_id = "1VFBDoJFy6yb9z7-luOwRv3fCmeNOzILPnR4QVmR0bGJ7gQ3QMPpCW-yt"
creds, _ = google.auth.default()
service = build("script", "v1", credentials=creds)
# Create an execution request object.
request = {"function": "getFoldersUnderRoot"}
try:
# Make the API request.
response = service.scripts().run(scriptId=script_id, body=request).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
# a list of stack trace elements.
error = response["error"]["details"][0]
print(f"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(f"\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 (folder_set).
folder_set = response["response"].get("result", {})
if not folder_set:
print("No folders returned!")
else:
print("Folders under your root folder:")
for folder_id, folder in folder_set.items():
print(f"\t{0} ({1}).{format(folder, folder_id)}")
except HttpError as error:
# The API encountered a problem before the script started executing.
print(f"An error occurred: {error}")
print(error.content)
if __name__ == "__main__":
main()
限制
Apps Script API 有几个限制:
一个常见的 Cloud 项目。被调用的脚本和 调用应用必须共享 Cloud 项目。Cloud 项目必须是 标准 Cloud 项目; 为 Apps 脚本项目创建的默认项目是不够的。通过 标准 Cloud 项目可以是新项目,也可以是现有项目。
基本参数和返回值类型。该 API 不能传递或返回 Apps 脚本特有的对象(例如 Documents、 Blob、 日历、 云端硬盘文件等)复制到 应用。仅限基本类型,如字符串、数组、对象、数字和 可以传递和返回布尔值。
OAuth 范围。该 API 只能执行 一个必需的范围。这意味着,您无法使用该 API 调用不需要一项或多项服务授权的脚本。
无触发器。API 无法创建 Apps 脚本 触发器。