Salesforce Composite API:集成工程师的深度解析与高效事务性集成实践
背景与应用场景
大家好,我是一名 Salesforce 集成工程师。在我的日常工作中,核心任务之一就是构建和维护 Salesforce 与外部系统之间高效、可靠的数据通道。在传统的 REST API 集成模式中,我们经常会遇到一个棘手的问题:“网络交互过于频繁” (Chattiness)。
想象一个常见的业务场景:当一个新客户在我们的电商网站下单时,我们需要在 Salesforce 中创建一条客户 (Account) 记录,然后为该客户创建一条联系人 (Contact) 记录,并最终生成一张订单 (Order) 记录。如果使用标准的 REST API,这将需要至少三次独立的网络请求:
1. 第一次 API 调用:创建一个 Account。
2. 等待第一次调用成功并返回 Account ID。
3. 第二次 API 调用:使用上一步获取的 Account ID 创建一个 Contact。
4. 第三次 API 调用:使用 Account ID 创建一个 Order。
这种模式不仅效率低下,因为每次网络往返都会产生延迟,而且还存在数据一致性风险。如果在创建 Contact 时发生故障,我们就可能在系统中留下一个没有关联联系人的“孤儿”客户记录。此外,每次 API 调用都会消耗宝贵的组织 API 调用限制。
为了解决这些集成挑战,Salesforce 提供了 Composite API (组合 API)。这是一个强大的 REST API 资源,它允许我们将多个独立的 API 请求捆绑到一个单一的 HTTP 调用中。作为一名集成工程师,我将 Composite API 视为我们工具箱中最关键的工具之一,它能够显著提升集成性能、保证数据事务的原子性,并简化客户端的逻辑。
典型应用场景包括:
- 复杂数据初始化:在一个操作中创建客户、其主要联系人、相关业务机会以及跟进任务,确保所有记录要么全部成功创建,要么全部失败回滚。
- 前端 UI 优化:当用户在自定义页面(如 LWC 或外部 Web 应用)上点击“保存”按钮时,该操作可能需要同时更新多个不同对象的数据。使用 Composite API 可以将这些更新打包成一个请求,为用户提供更流畅、响应更快的体验。
- 事务性数据集成:在与 ERP 系统同步订单数据时,可能需要创建订单头、多个订单行项目,并更新库存记录。Composite API 的事务性功能可以确保整个订单数据在 Salesforce 中的完整性和一致性。
原理说明
Composite API 的核心原理非常直观:它扮演了一个“请求协调器”的角色。你将一系列独立的子请求 (Subrequest) 组织在一个 JSON 结构中,然后将这个大的 JSON 作为一个 HTTP POST 请求的主体,发送到 Salesforce 的单个端点:/services/data/vXX.X/composite。
Salesforce 服务器在接收到这个请求后,会解析其中的子请求数组,并按照顺序依次执行它们。最精妙的部分在于,它支持在后续的子请求中引用前面子请求的执行结果(例如新创建记录的 ID),从而实现了请求之间的动态依赖关系。
一个标准的 Composite API 请求体主要由两个关键部分构成:
1. `allOrNone`: 这是一个布尔类型的属性,用于控制整个复合请求的事务行为。
- 如果设置为
true,则整个操作将以“全有或全无”的原子事务模式执行。只要其中任何一个子请求失败,所有已经成功执行的子请求所做的数据更改都将被完全回滚。这对于需要高度数据一致性的业务场景至关重要。 - 如果设置为
false,Salesforce 会尝试执行每一个子请求。成功的子请求将被提交,而失败的子请求则会被标记为失败,但不会影响其他成功的操作。这适用于允许部分成功的场景。
2. `compositeRequest`: 这是一个 JSON 数组,包含了所有需要执行的子请求。每个子请求都是一个 JSON 对象,通常包含以下属性:
method: HTTP 方法,例如 "POST", "PATCH", "GET", "DELETE"。url: 子请求的目标 REST API URL,例如/services/data/v58.0/sobjects/Account。注意,这里不包含 Salesforce 实例域名。referenceId: 这是一个你在请求中自定义的唯一标识符,用于标记这个子请求。它的作用至关重要,因为你可以通过这个 ID 在后续的子请求中引用当前请求的返回结果。body: 对于 POST 或 PATCH 请求,这里是请求的主体内容,与标准的 REST API 调用完全相同。
最核心的机制是 `referenceId` 和引用语法。 当一个子请求(例如,创建 Account)成功执行后,Salesforce 会在内存中保留其结果,包括新记录的 ID。在后续的子请求中,你可以使用 "@{referenceId.id}" 这样的语法来动态地注入这个 ID。例如,在创建 Contact 时,你可以将其 AccountId 字段的值设置为 "@{refAccount.id}",其中 `refAccount` 就是你为创建 Account 的子请求所指定的 referenceId。这彻底解决了传统模式下需要等待上一个请求返回才能发起下一个请求的依赖问题。
示例代码
让我们来看一个来自 Salesforce 官方文档的经典示例。这个示例将演示如何在一个 Composite API 调用中创建一个新的 Account,并立即创建一个与该新 Account 相关联的 Contact。这完美地展示了 `referenceId` 的强大之处。
请求 (Request)
这是一个发送到 POST /services/data/v58.0/composite 端点的 HTTP 请求体。我们将 `allOrNone` 设置为 `true` 以确保事务的完整性。
{
"allOrNone" : true,
"compositeRequest" : [{
"method" : "POST",
"url" : "/services/data/v58.0/sobjects/Account",
"referenceId" : "refAccount",
"body" : { "Name" : "Salesforce" }
},{
"method" : "POST",
"url" : "/services/data/v58.0/sobjects/Contact",
"referenceId" : "refContact",
"body" : {
"LastName" : "John Doe",
"AccountId" : "@{refAccount.id}"
}
}]
}
代码注释:
- 第 2 行:
"allOrNone" : true- 声明这是一个原子事务。如果创建 Contact 失败(例如,因为验证规则),那么已经创建的 Account 也会被回滚。 - 第 3 行:
"compositeRequest" : [...]- 定义子请求数组的开始。 - 第 4-8 行: 这是第一个子请求。
"method" : "POST"- 我们要创建一个新记录。"url" : "/services/data/v58.0/sobjects/Account"- 目标是 Account 对象。"referenceId" : "refAccount"- 我们为这个请求分配了一个引用 ID "refAccount"。这个 ID 是我们自己定义的,只要在当前复合请求中唯一即可。"body" : { "Name" : "Salesforce" }- 创建 Account 所需的数据。
- 第 9-16 行: 这是第二个子请求。
"method" : "POST","url" : "/services/data/v58.0/sobjects/Contact"- 类似于第一个请求,这次目标是 Contact 对象。"referenceId" : "refContact"- 为创建 Contact 的请求分配引用 ID "refContact"。"AccountId" : "@{refAccount.id}"- 这是整个示例的核心!我们正在设置 Contact 的 `AccountId` 字段。它的值不是一个硬编码的 ID,而是一个引用。@{refAccount.id}的意思是:“获取 `referenceId` 为 `refAccount` 的那个子请求的执行结果,并从结果中提取 `id` 属性的值”。这样,我们就动态地将这个新的 Contact 关联到了刚刚在同一次调用中创建的 Account 上。
成功响应 (Successful Response)
如果所有子请求都成功执行,Salesforce 会返回 HTTP 状态码 200 OK,响应体结构如下:
{
"compositeResponse" : [{
"body" : {
"id" : "001xx000003DHPF",
"success" : true,
"errors" : [ ]
},
"httpHeaders" : {
"Location" : "/services/data/v58.0/sobjects/Account/001xx000003DHPF"
},
"httpStatusCode" : 201,
"referenceId" : "refAccount"
},{
"body" : {
"id" : "003xx000004Wf8N",
"success" : true,
"errors" : [ ]
},
"httpHeaders" : {
"Location" : "/services/data/v58.0/sobjects/Contact/003xx000004Wf8N"
},
"httpStatusCode" : 201,
"referenceId" : "refContact"
}]
}
响应解析:
compositeResponse数组中的元素与请求中的compositeRequest数组一一对应,顺序和referenceId都保持一致。- 每个响应元素都包含了该子请求的执行结果,包括
httpStatusCode(例如 201 表示创建成功)、响应体body(其中包含了新记录的 ID)以及referenceId。 - 作为集成工程师,我们的代码需要解析这个响应数组,以确认每个步骤都已成功并获取新记录的 ID 用于后续处理。
注意事项
在使用 Composite API 时,有一些关键点需要特别注意,以确保集成的健壮性和可扩展性。
权限与数据可见性 (Permissions & Data Visibility)
整个 Composite API 请求是在发起调用的认证用户的上下文中执行的。这意味着,如果该用户没有创建 Account 对象的权限,那么第一个子请求就会失败,进而导致整个事务(当 allOrNone 为 `true` 时)失败。所有字段级安全性 (Field-Level Security)、对象权限、共享规则 (Sharing Rules) 和验证规则 (Validation Rules) 都会像在 Salesforce UI 中操作一样被严格执行。
API 限制 (API Limits)
这是一个非常重要的 нюанс。一个 Composite API 调用本身,无论包含多少个子请求,都只消耗一个 API 调用限额。这对于优化 API 使用量来说是一个巨大的优势。但是,需要注意以下几点:
- 子请求数量限制:一个 Composite API 请求中最多可以包含 25 个子请求。
- 内部调控限制 (Governor Limits):虽然只算一次 API 调用,但所有子请求的操作共享同一个事务的 Governor Limits。例如,所有子请求执行的 DML 语句总数不能超过 150 条,SOQL 查询总数不能超过 100 次。对于集成工程师来说,这意味着我们不能用它来替代 Bulk API 处理成千上万条记录的场景。
错误处理 (Error Handling)
错误处理策略完全取决于 allOrNone 的设置。
- 当 `allOrNone` 为 `true` 时:任何子请求的失败都会导致 Salesforce 返回一个顶级的 HTTP
400 Bad Request状态码。响应体将明确指出是哪个子请求(通过 `referenceId`)失败了以及失败的原因。此时,你需要知道整个事务已经被回滚。 - 当 `allOrNone` 为 `false` 时:即使有部分子请求失败,顶级的 HTTP 状态码通常也是
200 OK。你必须 遍历响应体中的compositeResponse数组,并检查每一个元素的httpStatusCode。任何非 2xx 的状态码(如 400 或 500)都表示该子请求失败。你的集成代码必须能够优雅地处理这种部分成功、部分失败的场景。
局限性 (Limitations)
Composite API 非常强大,但并非万能。它主要支持 sObject 相关的 REST API 资源,如 sObject Create, Retrieve, Update, Delete (CRUD) 和 Query。你不能在子请求中调用 Metadata API、Tooling API 或其他一些复杂的 API 资源。它的设计初衷是优化数据操作(DML)和查询(SOQL)的组合,而不是管理元数据或执行复杂的系统任务。
总结与最佳实践
对于 Salesforce 集成工程师而言,Composite API 是解决特定集成模式的“瑞士军刀”。它通过将多个请求捆绑成一个原子事务,优雅地解决了网络延迟、API 限额消耗和数据一致性三大难题。
总结其核心优势:
- 性能提升:显著减少了客户端与服务器之间的网络往返次数,降低了总延迟。
- 事务完整性:通过 `allOrNone` 标志,提供了数据库级别的原子性保证,避免了数据不一致的状态。
- 简化逻辑:将复杂的依赖关系处理从客户端代码转移到了 Salesforce 服务器端,让集成逻辑更清晰。
- 高效利用限额:将多达 25 个操作合并为一次 API 调用,极大地节省了 API 调用限额。
作为最佳实践,我建议遵循以下原则:
场景驱动:优先为那些需要在一个业务流程中操作多个相关记录的场景选择 Composite API。
审慎使用 `allOrNone`:根据业务对数据一致性的要求来决定。关键的财务或订单处理流程应始终使用 `true`,而一些辅助性的、允许部分失败的更新(如记录日志)则可以使用 `false`。
健壮的错误处理:无论 `allOrNone` 如何设置,你的代码都应该能够解析响应体,并对成功和失败的子请求做出相应的处理。特别是当 `allOrNone` 为 `false` 时,必须实现补偿逻辑或重试机制来处理失败的子请求。
明确边界:Composite API 适用于复杂的、中等规模的事务性操作。对于需要处理数千乃至数百万条记录的大规模数据加载或提取,请务必使用 Bulk API 2.0,它是为此类场景专门设计的。
版本化你的 API 调用:始终在 URL 中明确指定 API 版本(如
v58.0)。这可以防止未来 Salesforce 版本更新对你的集成产生意外的破坏性影响,确保了长期稳定性。
通过深入理解并恰当应用 Composite API,我们可以构建出更高效、更可靠、更具扩展性的 Salesforce 集成解决方案。
评论
发表评论