- 入门指南
- 最佳实践
- 租户
- 注册表
- Cloud Robots
- Automation Suite 机器人
- 文件夹上下文
- 流程
- 作业
- Apps
- 触发器
- 日志
- 监控
- 索引
- 队列
- 资产
- 连接
- 业务规则
- 存储桶
- MCP 服务器
- Orchestrator 测试
- 资源目录服务
- 集成
- 故障排除
用于在 Orchestrator 中通过 HTTP 创建和跟踪作业执行的 API 触发器调用模式概述。
API 触发器调用模式专门用于在 Orchestrator 中创建和跟踪作业执行。
基于 HTTP 的通信的根本挑战在于它本质上是同步的:通常,客户端会发送请求并等待来自服务器的响应。但是,在机器人作业的上下文中,Orchestrator 不支持在作业完成之前使连接保持打开。
To work around this, we’ve used the HTTP protocol to model multiple call modes. These modes rely on standard HTTP methods, status codes, and headers to create, monitor and get the result of jobs within the system, without unnecessarily keeping the connection open.
Each of the proposed call modes has advantages and disadvantages, so you can choose the one that is best suited for your integration needs. Bear in mind that, to ensure secure communication, each call requires proper authentication through bearer tokens.
已知问题 请求有时可能会返回 502 Bad Gateway Cloudflare 错误,这是由于大量连接在较长的时间间隔内保持活跃状态而导致的。尽管出现错误,此类请求的基础作业可能已在运行,因此您可以在 Orchestrator 中检查其状态。此问题是间歇性问题,所有后续请求都会正常运行。
异步轮询
此调用模式涉及使用预配置的 HTTP 谓词发出调用,以触发新作业并接收状态 URI。然后,必须手动轮询状态 URI,直到作业完成。此时,调用将重定向到另一个端点,该端点用于检索作业结果(输出或错误)。
图 1. 异步轮询工作流
系统将使用已配置的 HTTP 谓词(GET、POST、PUT、DELETE)发出初始调用,这会在 Orchestrator 中创建关联的作业。成功创建作业后,系统将在 Location 标头中返回 HTTP 状态代码 202(已接受)和状态 URI 作为响应。
Once the job is created, you are expected to periodically poll its status. During this polling process, as long as the job is running, each GET request to the status URI returns an HTTP status code 200 (OK). When the job is completed, the next GET request to the status URI returns an HTTP status code 303 (See Other), redirecting to the output URI (via the Location header).
You are expected to follow the output URI to retrieve the job result (output or error).
始终确保在每次调用的标头中包含有效的持有者令牌,以便成功进行身份验证。
JavaScript 示例
此示例说明如何从浏览器中,通过 API 触发器使用“异步轮询”调用模式启动作业。
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const url = '{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>';
const token = '<PERSONAL_ACCESS_TOKEN>'; // could also be an access token retrieved via OAuth
const body = {
'argument1': 123,
'argument2': 'my string',
'$callMode': 'AsyncRequestReply' // optional argument to force call mode to AsyncRequestReply
}
// if invocation is done by GET, place the parameters in query string and remove the 'Content-Type' header
const invokeRequestOptions = {
method: 'POST',
headers: new Headers({ 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }),
body: JSON.stringify(body),
};
const redirectRequestOptions = {
method: "GET",
credentials: 'same-origin',
headers: new Headers({ Authorization: `Bearer ${token}` }),
redirect: "follow", // this option must be set to follow, otherwise an 'opaqueredirect' response is returned
};
const response = await fetch(url, invokeRequestOptions);
let newLocation = response.headers.get("Location");
// first response should be 202 and have a location header
console.log(`Got ${response.status}, with location: ${newLocation}`);
for (let i = 0; i < 20; i++) {
await sleep(SLEEP_DURATION);
// follow the location header to the new endpoint
const statusResponse = await fetch(newLocation, redirectRequestOptions);
// if the response was redirected (and automatically followed), then the output endpoint has been reached
// the output of the job can be found in the body of the response in JSON format
if (statusResponse.status != 200 || statusResponse.redirected)
{
// read the job output
const output = await statusResponse.json();
console.log(`Got ${statusResponse.status}, with body: ${JSON.stringify(output)}`);
break;
}
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const url = '{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>';
const token = '<PERSONAL_ACCESS_TOKEN>'; // could also be an access token retrieved via OAuth
const body = {
'argument1': 123,
'argument2': 'my string',
'$callMode': 'AsyncRequestReply' // optional argument to force call mode to AsyncRequestReply
}
// if invocation is done by GET, place the parameters in query string and remove the 'Content-Type' header
const invokeRequestOptions = {
method: 'POST',
headers: new Headers({ 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }),
body: JSON.stringify(body),
};
const redirectRequestOptions = {
method: "GET",
credentials: 'same-origin',
headers: new Headers({ Authorization: `Bearer ${token}` }),
redirect: "follow", // this option must be set to follow, otherwise an 'opaqueredirect' response is returned
};
const response = await fetch(url, invokeRequestOptions);
let newLocation = response.headers.get("Location");
// first response should be 202 and have a location header
console.log(`Got ${response.status}, with location: ${newLocation}`);
for (let i = 0; i < 20; i++) {
await sleep(SLEEP_DURATION);
// follow the location header to the new endpoint
const statusResponse = await fetch(newLocation, redirectRequestOptions);
// if the response was redirected (and automatically followed), then the output endpoint has been reached
// the output of the job can be found in the body of the response in JSON format
if (statusResponse.status != 200 || statusResponse.redirected)
{
// read the job output
const output = await statusResponse.json();
console.log(`Got ${statusResponse.status}, with body: ${JSON.stringify(output)}`);
break;
}
C# 示例
此示例说明如何从基于 C# 的应用程序中,通过 API 触发器使用“异步轮询”调用模式启动作业。
public async Task<string> AsyncPollingExample()
{
const string url = "{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>";
const string token = "<PERSONAL_ACCESS_TOKEN>"; // could also be an access token retrieved via OAuth
// create an http client that does not follow redirects and adds a bearer token on each call
var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var arguments = new Dictionary<string, object>
{
{ "argument1", 123 },
{ "argument2", "my string" },
{ "$callMode", "AsyncRequestReply" }, // optional argument to force call mode to AsyncRequestReply
};
var httpResponseStart = await httpClient.PostAsJsonAsync(url, arguments);
var redirectUri = httpResponseStart.Headers.Location;
if (httpResponseStart.StatusCode != HttpStatusCode.Accepted)
throw new Exception("Could not invoke workflow");
while (true)
{
var httpPollingResponse = await httpClient.GetAsync(redirectUri);
if (httpPollingResponse.StatusCode == HttpStatusCode.Redirect)
{
var outputLocation = httpPollingResponse.Headers.Location;
var outputResponse = await httpClient.GetAsync(outputLocation);
var jobOutput = await outputResponse.Content.ReadAsStringAsync();
return jobOutput;
}
await Task.Delay(1000);
}
}
public async Task<string> AsyncPollingExample()
{
const string url = "{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>";
const string token = "<PERSONAL_ACCESS_TOKEN>"; // could also be an access token retrieved via OAuth
// create an http client that does not follow redirects and adds a bearer token on each call
var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var arguments = new Dictionary<string, object>
{
{ "argument1", 123 },
{ "argument2", "my string" },
{ "$callMode", "AsyncRequestReply" }, // optional argument to force call mode to AsyncRequestReply
};
var httpResponseStart = await httpClient.PostAsJsonAsync(url, arguments);
var redirectUri = httpResponseStart.Headers.Location;
if (httpResponseStart.StatusCode != HttpStatusCode.Accepted)
throw new Exception("Could not invoke workflow");
while (true)
{
var httpPollingResponse = await httpClient.GetAsync(redirectUri);
if (httpPollingResponse.StatusCode == HttpStatusCode.Redirect)
{
var outputLocation = httpPollingResponse.Headers.Location;
var outputResponse = await httpClient.GetAsync(outputLocation);
var jobOutput = await outputResponse.Content.ReadAsStringAsync();
return jobOutput;
}
await Task.Delay(1000);
}
}
异步触发并忘记
成功创建作业后,“触发与忽略”调用模式将返回 200 OK 状态,但不会返回有关该作业的任何其他信息。
图 2. “异步触发并忘记”工作流
JavaScript 示例
此示例说明如何从浏览器中,通过 API 触发器使用“异步触发与忽略”调用模式启动作业。
const url = '{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>';
const token = '<PERSONAL_ACCESS_TOKEN>'; // could also be an access token retrieved via OAuth
const body = {
'argument1': 123,
'argument2': 'my string',
'$callMode': 'FireAndForget' // optional argument to force call mode to FireAndForget
}
const options = {
method: 'POST',
headers: new Headers({ 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }),
body: JSON.stringify(body),
};
let response = await fetch(url, options);
console.log(`Got ${response.status}`);
const url = '{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>';
const token = '<PERSONAL_ACCESS_TOKEN>'; // could also be an access token retrieved via OAuth
const body = {
'argument1': 123,
'argument2': 'my string',
'$callMode': 'FireAndForget' // optional argument to force call mode to FireAndForget
}
const options = {
method: 'POST',
headers: new Headers({ 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }),
body: JSON.stringify(body),
};
let response = await fetch(url, options);
console.log(`Got ${response.status}`);
C# 示例
此示例说明如何从基于 C# 的应用程序中,通过 API 触发器使用“异步触发与忽略”调用模式启动作业。
public async Task FireAndForgetExample()
{
const string url = "{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>";
const string token = "<PERSONAL_ACCESS_TOKEN>"; // could also be an access token retrieved via OAuth
// create an http client that does not follow redirects and adds Bearer <token> on each call
// if the follow redirects option is enabled, C# will not add a bearer token by default after the redirect
var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var arguments = new Dictionary<string, object>
{
{ "argument1", 123 },
{ "argument2", "my string" },
{ "$callMode", "FireAndForget" }, // optional argument to force call mode to LongPolling
};
var response = await httpClient.PostAsJsonAsync(url, arguments);
Console.WriteLine(response.StatusCode);
}
public async Task FireAndForgetExample()
{
const string url = "{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>";
const string token = "<PERSONAL_ACCESS_TOKEN>"; // could also be an access token retrieved via OAuth
// create an http client that does not follow redirects and adds Bearer <token> on each call
// if the follow redirects option is enabled, C# will not add a bearer token by default after the redirect
var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var arguments = new Dictionary<string, object>
{
{ "argument1", 123 },
{ "argument2", "my string" },
{ "$callMode", "FireAndForget" }, // optional argument to force call mode to LongPolling
};
var response = await httpClient.PostAsJsonAsync(url, arguments);
Console.WriteLine(response.StatusCode);
}
同步 (长轮询)
此调用模式涉及一次初始调用,该调用会阻止并等待一段时间让作业完成,然后进行几次可能的阻塞调用和重定向。最后,在作业完成后,检索作业结果(输出或错误)。
根据租户设置,可能需要对所有调用进行身份验证,也可能只需要对初始调用进行身份验证。
图 3. 同步(长时间轮询)工作流,其中结果与初始调用一起返回
图 4. 同步(长时间轮询)工作流,其中会自动进行多次调用以完成作业
系统将使用已配置的 HTTP 谓词(GET、POST、PUT、DELETE)发出初始调用,这会在 Orchestrator 中创建关联的作业。成功创建作业后,系统会在等待作业完成的同时阻止当前调用。作业完成后,系统将释放阻止的调用,并发回包含作业输出参数的响应。
If after a timeout period the job has not yet completed, the system responds with an HTTP status code 303 (See Other), redirecting to the status URI (via the Location header). You are expected to follow the status URI, which is blocked until the job is completed.
If the job is not completed after a timeout, you are yet again redirected to the status URI, thus creating a redirect loop. Upon successful completion of the job, the job result (output or error) is retrieved as part of the HTTP response.
默认情况下,所有调用都需要包含有效的持有者令牌以进行授权。但是,如果未选择租户设置选项“同步 API 触发器重定向需要身份验证标头” ,则只有对触发器的初始调用才需要身份验证标头。无需授权标头即可对状态端点进行后续调用。
此调用模式的最长作业持续时间为 15 分钟。
JavaScript 示例
此示例说明如何从浏览器中,通过 API 触发器使用“同步 (长轮询)”调用模式启动作业。
const url = '{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>';
const token = '<PERSONAL_ACCESS_TOKEN>'; // could also be an access token retrieved via OAuth
const body = {
'argument1': 123,
'argument2': 'my string',
'$callMode': 'LongPolling' // optional argument to force call mode to LongPolling
}
const options = {
method: 'POST',
headers: new Headers({ 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }),
body: JSON.stringify(body),
redirect: "follow", // follow redirects automatically
};
let response = await fetch(url, options);
const output = await response.json();
console.log(`Got ${response.status} with body ${JSON.stringify(output)}`);
const url = '{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>';
const token = '<PERSONAL_ACCESS_TOKEN>'; // could also be an access token retrieved via OAuth
const body = {
'argument1': 123,
'argument2': 'my string',
'$callMode': 'LongPolling' // optional argument to force call mode to LongPolling
}
const options = {
method: 'POST',
headers: new Headers({ 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }),
body: JSON.stringify(body),
redirect: "follow", // follow redirects automatically
};
let response = await fetch(url, options);
const output = await response.json();
console.log(`Got ${response.status} with body ${JSON.stringify(output)}`);
C# 示例
此示例说明如何从基于 C# 的应用程序中,通过 API 触发器使用“同步 (长轮询)”调用模式启动作业。
public async Task SyncExample()
{
const string url = "{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>";
const string token = "<PERSONAL_ACCESS_TOKEN>"; // could also be an access token retrieved via OAuth
// create an http client that does not follow redirects and adds Bearer <token> on each call
// if the follow redirects option is enabled, C# will not add a bearer token by default after the redirect
var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var arguments = new Dictionary<string, object>
{
{ "argument1", 123 },
{ "argument2", "my string" },
{ "$callMode", "LongPolling" }, // optional argument to force call mode to LongPolling
};
var response = await httpClient.PostAsJsonAsync(url, arguments);
while(response.StatusCode == HttpStatusCode.SeeOther)
{
// in case of redirection, keep following the latest location in the header
var location = response.Headers.Location;
response = await httpClient.GetAsync(location);
}
// read the job output/error from the last request
var jobOutput = response.Content.ReadAsStringAsync();
Console.WriteLine(jobOutput);
}
public async Task SyncExample()
{
const string url = "{AutomationCloudURL}/{organizationName}/{tenantName}/orchestrator_/t/<INVOKE_URL>";
const string token = "<PERSONAL_ACCESS_TOKEN>"; // could also be an access token retrieved via OAuth
// create an http client that does not follow redirects and adds Bearer <token> on each call
// if the follow redirects option is enabled, C# will not add a bearer token by default after the redirect
var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false });
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var arguments = new Dictionary<string, object>
{
{ "argument1", 123 },
{ "argument2", "my string" },
{ "$callMode", "LongPolling" }, // optional argument to force call mode to LongPolling
};
var response = await httpClient.PostAsJsonAsync(url, arguments);
while(response.StatusCode == HttpStatusCode.SeeOther)
{
// in case of redirection, keep following the latest location in the header
var location = response.Headers.Location;
response = await httpClient.GetAsync(location);
}
// read the job output/error from the last request
var jobOutput = response.Content.ReadAsStringAsync();
Console.WriteLine(jobOutput);
}