
Orchestrator 用户指南
Webhook 用于在整个应用程序生态系统中更出色地集成 UiPath™ 自动化。您可以订阅 Orchestrator 事件并将其发送到任何外部 DCM、BPM 或 CRM 解决方案,同时通知不同的用户存在可供处理的新队列项目、触发器失败或已更新流程等事项。
Webhook 允许外部系统订阅和侦听不同类型的 Orchestrator 事件。Webhooks 页面使您可以轻松地设置它们,以及查看先前创建的内容。您还可以禁用 Webhook,搜索特定的 Webhook,编辑或删除 Webhook。
事件适用于作业、机器人、队列、队列项目、流程和计划。有关事件类型的完整列表和一些示例,请查看此页面。
每个事件都会向指定的 URL 发送包含信息的有效负载。一些属性是所有事件的通用属性,而另一些属性则特定于每种事件类型。
Webhook 事件按文件夹创建,因此,如果您有一个 Webhook 事件与在文件夹之间共享的资源(例如队列)相关联,则系统将为每个文件夹生成一个单独的 Webhook 事件。
如果转发事件的请求失败,则该特定 Webhook 的断开器会打开,从而禁用 Webhook 一小时。
- 系统将跳过本应在断开器打开时发送的任何 Webhook 事件,并且在开关关闭后不会重试该事件。
- 系统不会存储 Webhook 事件,因此无法重试或导出 Webhook 事件。此外,如果对外部平台的调用失败,则事件将丢失。Webhook 专为实时处理而设计。
| 属性名称 | 属性类型 | 说明和示例 | 
|---|---|---|
| 名称 | 字符串 | 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-SignatureHTTP 标头。
- 要获取原始签名字节,请从 Base64 解码标头的值。
- 
                     检索原始请求正文。 注意:Orchestrator 请求始终使用 UTF-8 编码。
- 使用 SHA256 和签名密钥(UTF-8 编码)计算哈希值。
- 
                     将计算得出的签名与X-UiPath-SignatureHTTP 标头中的值进行比较:- 如果签名不匹配,则不会处理请求。
- 如果签名匹配,则客户端应用程序应处理请求。
 
签名验证示例
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)