Webhooks
Webhook 用于在整个应用程序生态系统中更出色地集成 UiPath™ 自动化。您可以订阅 Orchestrator 事件并将其发送到任何外部 DCM、BPM 或 CRM 解决方案,同时通知不同的用户存在可供处理的新队列项目、触发器失败或已更新流程等事项。
Webhook 允许外部系统订阅和侦听不同类型的 Orchestrator 事件。Webhooks 页面使您可以轻松地设置它们,以及查看先前创建的内容。您还可以禁用 Webhook,搜索特定的 Webhook,编辑或删除 Webhook。
事件适用于作业、机器人、队列、队列项目、流程和计划。有关事件类型的完整列表和一些示例,请查看此页面。
每个事件都会向指定的 URL 发送包含信息的有效负载。一些属性是所有事件的通用属性,而另一些属性则特定于每种事件类型。
Webhook 事件按文件夹创建,因此,如果您有一个 Webhook 事件与在文件夹之间共享的资源(例如队列)相关联,则系统将为每个文件夹生成一个单独的 Webhook 事件。
如果转发事件的请求失败,则该特定 Webhook 的断开器会打开,从而禁用 Webhook 一小时。
- 系统将跳过本应在断开器打开时发送的任何 Webhook 事件,并且在开关关闭后不会重试该事件。
- 系统不会存储 Webhook 事件,因此无法重试或导出 Webhook 事件。此外,如果对外部平台的调用失败,则事件将丢失。Webhook 专为实时处理而设计。
属性名称 |
属性类型 |
说明和示例 |
---|---|---|
类型 |
字符串 |
触发通知的事件类型。 对于所有类型的事件,都会显示此属性。 示例:
|
活动 ID |
字符串 |
发生时为每个事件生成的唯一标识符。 对于所有类型的事件,都会显示此属性。 示例:
|
时间戳 |
RFC 8601 日期 |
生成事件的日期和时间。 对于所有类型的事件,都会显示此属性。 示例:
|
租户 ID |
整数 |
在其中生成事件的租户的 ID。默认租户为 1。 对于所有类型的事件,都会显示此属性。 示例:
|
用户 ID |
整数 |
操作触发事件的用户 ID。 如果事件是由机器人或触发器触发的,则不会显示此参数。 对于所有类型的事件,都会显示此属性。 示例:
|
文件夹 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)