Webhooks
Webhook 用于在整个应用程序生态系统中更出色地集成 UiPath™ 自动化。您可以订阅 Orchestrator 事件并将其发送到任何外部 DCM、BPM 或 CRM 解决方案,同时通知不同的用户存在可供处理的新队列项目、触发器失败或已更新流程等事项。
Webhook 允许外部系统订阅和侦听不同类型的 Orchestrator 事件。Webhooks 页面使您可以轻松地设置它们,以及查看先前创建的内容。您还可以禁用 Webhook,搜索特定的 Webhook,编辑或删除 Webhook。
事件适用于作业、机器人、队列、队列项目、流程和计划。有关事件类型的完整列表和一些示例,请查看此页面。
每个事件都会向指定的 URL 发送包含信息的有效负载。一些属性是所有事件的通用属性,而另一些属性则特定于每种事件类型。
Webhook 事件按文件夹创建,因此,如果您有一个 Webhook 事件与在文件夹之间共享的资源(例如队列)相关联,则系统将为每个文件夹生成一个单独的 Webhook 事件。
If the request to forward an event fails, the circuit breaker for that particular webhook opens, disabling the webhook for one hour.
- Any webhook events that should have been sent while the circuit breaker is open are skipped and not retried once the circuit breaker closes.
- Webhook events are not stored, and cannot be retried or exported. Furthermore, if the call to the external platform fails, the event is lost. Webhooks are designed for real-time processing.
属性名称 |
属性类型 |
说明和示例 |
---|---|---|
名称 |
字符串 |
Webhook 的名称。 系统将为所有类型的事件显示此属性,并且此属性为必要项。 示例:
|
类型 |
字符串 |
触发通知的事件类型。 对于所有类型的事件,都会显示此属性。 示例:
|
活动 ID |
字符串 |
发生时为每个事件生成的唯一标识符。 对于所有类型的事件,都会显示此属性。 示例:
|
时间戳 |
RFC 8601 日期 |
生成事件的日期和时间。 对于所有类型的事件,都会显示此属性。 示例:
|
租户 ID |
整数 |
在其中生成事件的租户的 ID。默认租户为 1。 对于所有类型的事件,都会显示此属性。 示例:
|
用户 ID |
整数 |
操作触发事件的用户 ID。 如果事件是由机器人或触发器触发的,则不会显示此参数。 对于所有类型的事件,都会显示此属性。 示例:
|
要在“Webhook”页面上执行各种操作,需要获得对 Webhook 的相应权限:
- 查看 - 使您能够查看 Webhook 及其详细信息,以及使用 API 对其进行检索,发送 ping 请求或获取 Webhook 可以订阅的所有事件的列表。
- 创建- 此权限使您可以添加新的 Webhook。请注意,您还需要查看权限。
- 编辑 - 使您能够从用户界面或使用 API 编辑 Webhook。请注意,您还需要“查看”权限。
- 删除 - 此权限使您可以删除 Webhook。请注意,您还需要查看权限。
X-UiPath-Signature
HTTP 标头发送。
接收 Orchestrator 请求的客户端应用程序必须检查请求的真实性。请求签名遵循以下模式:
- 客户端应用程序收到 Orchestrator 发出的 Webhook 请求;
- 客户端应用程序根据请求计算签名;
-
客户端应用程序尝试将其计算的签名与请求签名进行匹配:
- 如果签名不匹配,则客户端应用程序不应处理请求。
- 如果签名匹配,则客户端应用程序应处理请求。
签名计算应按以下步骤完成:
- 检索
X-UiPath-Signature
HTTP 标头。 - 要获取原始签名字节,请从 Base64 解码标头的值。
-
检索原始请求正文。
注意:Orchestrator 请求始终使用 UTF-8 编码。 - 使用 SHA256 和签名密钥(UTF-8 编码)计算哈希值。
-
将计算得出的签名与
X-UiPath-Signature
HTTP 标头中的值进行比较:- 如果签名不匹配,则不会处理请求。
- 如果签名匹配,则客户端应用程序应处理请求。
using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
public async Task<bool> IsValidRequestAsync(HttpRequestMessage request, string secret)
{
if (!request.Headers.TryGetValues("X-UiPath-Signature", out var headerValues))
return false;
var orchestratorSignature = Convert.FromBase64String(headerValues.First());
using (var sha = new HMACSHA256(key: Encoding.UTF8.GetBytes(secret)))
{
var computedSignature = sha.ComputeHash(await request.Content.ReadAsByteArrayAsync());
return ByteArrayEquals(orchestratorSignature, computedSignature);
}
}
using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
public async Task<bool> IsValidRequestAsync(HttpRequestMessage request, string secret)
{
if (!request.Headers.TryGetValues("X-UiPath-Signature", out var headerValues))
return false;
var orchestratorSignature = Convert.FromBase64String(headerValues.First());
using (var sha = new HMACSHA256(key: Encoding.UTF8.GetBytes(secret)))
{
var computedSignature = sha.ComputeHash(await request.Content.ReadAsByteArrayAsync());
return ByteArrayEquals(orchestratorSignature, computedSignature);
}
}
const { createServer } = require('http');
const { createHmac } = require('crypto');
const PORT = 9090
const WEBHOOK_SECRET = '<same secret as configured in Orchestrator>'
const isValidRequest = (body /* Buffer */, secret /* string */, expectedSignature /* string */) =>
expectedSignature == null || createHmac('sha256', secret)
.update(body)
.digest('base64') === expectedSignature
const server = createServer((req, resp) => {
let body = new Buffer([])
req.on('data', chunk => body = Buffer.concat([body, chunk]))
req.on('end', () => {
if (!isValidRequest(body, WEBHOOK_SECRET, req.headers['x-uipath-signature'])) {
console.error('Invalid signature')
resp.statusCode = 401 // Unauthorized
} else {
let payload = JSON.parse(body.toString('utf8'))
// Process request
console.log(payload)
resp.statusCode = 202 // Accepted
}
resp.end()
})
})
server.listen(PORT)
const { createServer } = require('http');
const { createHmac } = require('crypto');
const PORT = 9090
const WEBHOOK_SECRET = '<same secret as configured in Orchestrator>'
const isValidRequest = (body /* Buffer */, secret /* string */, expectedSignature /* string */) =>
expectedSignature == null || createHmac('sha256', secret)
.update(body)
.digest('base64') === expectedSignature
const server = createServer((req, resp) => {
let body = new Buffer([])
req.on('data', chunk => body = Buffer.concat([body, chunk]))
req.on('end', () => {
if (!isValidRequest(body, WEBHOOK_SECRET, req.headers['x-uipath-signature'])) {
console.error('Invalid signature')
resp.statusCode = 401 // Unauthorized
} else {
let payload = JSON.parse(body.toString('utf8'))
// Process request
console.log(payload)
resp.statusCode = 202 // Accepted
}
resp.end()
})
})
server.listen(PORT)