部署和检索元数据

使用 deploy() 和 retrieve() 调用移动元数据(XML 文件) 在 Salesforce 组织和本地文件系统之间。检索 XML 文件后 在文件系统中,您可以在源代码控制系统中管理更改,复制和粘贴 代码或设置配置,对组件进行差异更改,并执行许多其他基于文件的操作 开发操作。您可以随时将这些更改部署到另一个 Salesforce 组织。

注意

Ant 迁移工具使用 deploy() 和 retrieve() 调用来移动元数据。 如果您使用这些工具,则与元数据 API 的交互是无缝且不可见的。因此 大多数开发人员发现使用这些工具比编写直接调用 deploy() 和 retrieve() 的代码要容易得多。

XML 文件中的数据使用英语(美国)区域设置进行格式设置。此格式 确保对依赖于区域设置的字段(如日期字段)的解释一致 在使用不同语言的组织之间进行数据迁移期间。组织可以 支持多种语言向用户展示。

deploy() 和 retrieve() 调用主要用于 以下开发方案:

  • 在沙盒组织中开发自定义应用程序(或自定义)。后 完成开发和测试,然后部署应用程序或自定义项 使用元数据 API 进入生产组织。
  • 在 Developer Edition 组织中对应用程序进行团队开发。开发后 并完成测试,然后您可以通过 Lightning Platform 分发应用程序 AppExchange。

每次检索到最大数量的 90% 或更多时,您都会收到 API 通知 您可以使用元数据 API 一次性部署的自定义字段。自定义的最大数量 一个部署的字段为 45,000。在一个包.xml文件中检索到的自定义字段包括: 1) package.xml 的 CustomObjects 部分中每个对象的字段总和和 2) package.xml 的 CustomFields 部分中自定义字段的总和。

您仍然可以检索超过可部署最大值,直至达到检索文件总大小的限制。但是你 必须使用多个部署来部署所有自定义字段。

警告:您已检索到 47,000 个 CustomField 实例。无法重新部署所有这些 实例;最大值为 45,000。

使用 Zip 文件部署和检索元数据

和调用用于部署和检索 .zip 文件。在 .zip 文件是一个项目清单 (package.xml),其中列出了要执行的操作 检索或部署,以及组织到文件夹中的一个或多个 XML 组件。

deploy()retrieve()

注意

组件是元数据类型的实例。例如,是自定义对象的元数据类型,并且 该组件是 自定义对象。CustomObjectMyCustomObject__c

在 .zip 文件中检索或部署的文件可能是未打包的组件 驻留在组织中(例如标准对象)或打包组件 驻留在命名包中。

注意

您可以部署或 一次最多可检索 10,000 个文件。AppExchange 软件包使用不同的限制: 最多可包含 35,000 个文件。已部署或检索的 .zip 的最大大小 文件大小为 39 MB。如果文件在解压缩的文件夹中解压缩,则大小限制 是 400 MB。

  • 如果使用 Ant 迁移工具执行 部署解压后的文件夹,首先压缩文件夹中的所有文件。这 解压缩文件夹中未压缩组件的最大大小为 400 MB 或更少 取决于压缩比。如果文件具有高压缩比, 您总共可以迁移大约 400 MB,因为压缩的大小 将小于 39 MB。但是,如果组件不能被压缩太多,比如 二进制静态资源,可以迁移小于 400 MB。
  • 元数据 API base-64 对组件进行编码 它们被压缩了。生成的 .zip 文件不能超过 50 MB,即 SOAP 消息的限制。Base-64 编码会增加有效负载的大小,因此 在编码之前,压缩的有效负载不能超过大约 39 MB。
  • 您可以执行大 对象仅在定义其索引时才被定义。如果在安装程序中创建了一个大对象,并且 尚未定义索引,则无法检索它。retrieve()

每个 .zip 文件都包含一个项目清单、一个名为 package.xml 的文件以及一组包含 组件。清单文件定义您尝试执行的组件 在 .zip 文件中检索或部署。清单还定义了 API 版本 用于部署或检索。

注意

您可以编辑项目 清单,但如果您修改它所包含的组件列表,请小心。当你 部署或检索组件时,元数据 API 会引用 清单,而不是 .zip 文件中的目录。

下面是一个示例包 .xml 文件。您可以检索 通过在元素中指定元数据类型的 fullName 字段值来获取元数据类型的单个组件。您还可以 使用 检索元数据类型的所有组件。members<members>*</members>

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>MyCustomObject__c</members>
        <name>CustomObject</name>
    </types>
    <types>
        <members>*</members>
        <name>CustomTab</name>
    </types>
    <types>
        <members>Standard</members>
        <name>Profile</name>
    </types>
    <version>59.0</version>
</Package>

可以在 package.xml 中定义以下元素。

  • <fullName>包含服务器端的名称 包。如果不存在,则 package.xml 定义客户端包。<fullName>unpackaged
  • <types>包含元数据类型的名称 (例如,) 和 成员(例如,) 为 检索或部署。您可以在清单文件中添加多个元素。CustomObjectmyCustomObject__c<types>
  • <members>包含组件的 fullName,例如 。listMetadata() 调用可用于 确定特定组件的 fullName 元数据类型(如果要检索单个组件)。对于许多元数据 类型,您可以将 中的值替换为通配符(星号) 而不是单独列出每个成员。有关特定 type 以确定该类型是否支持通配符。 每个组件 在元数据 API 部署中必须具有唯一的名称。解析为 重复项,例如一个符号和 UTF-8 编码符号以及一对名为 xyz.typename 和 xyz.typename-meta.xml 仍然是 重复。MyCustomObject__cmembers*注意在元素中指定 Security,在名称中指定 Settings 元素。<members>
  • <name>包含元数据类型,例如 或 。在 目录。任何扩展元数据的元数据类型都是有效值。输入的名称 必须与元数据 API WSDL 中定义的元数据类型匹配。有关列表,请参阅元数据类型。CustomObjectProfile
  • <version>是使用的 API 版本号 部署或检索 .zip 文件时。当前有效值为 。59.0

有关演示如何工作的更多示例包 .xml 清单文件 使用不同的元数据子集,请参阅示例包 .xml 清单 文件。

要删除组件,请参阅从 组织。

部署缓慢

如果在服务器停机期间进行基于文件的元数据 API 部署,例如 Salesforce 服务升级时,部署所需的时间可能比预期的要长。发生此行为 因为组件部署和验证都是从头开始重试的,所以在 服务已恢复。但是,如果 Apex 测试是部署的一部分,则只有不是部署的测试 在停机时间运行之前运行。

此行为会影响基于文件的部署和检索、更改集、某些包安装 和升级、第二代托管软件包创建以及部署和检索已开始 从 Salesforce CLI 或 Salesforce VS Code 扩展。它不影响基于 CRUD 元数据操作。

如果您的实例需要进行计划的服务升级,请避免在 服务升级。要检查您的 Salesforce 实例是否需要升级,请检查 Salesforce 信任。Salesforce 每年执行 3 次重大服务升级和其他 全年维护更新。

检索作业的状态是否为“挂起”?

如果为单个启动多个并发检索操作 org,元数据 API 会自动将其中一些作业放入队列中,如果有必要的话 服务保护。如果检索作业的状态为 ,则为 在队列中。当其中一个活动检索作业完成时,元数据 API 将接受挂起的作业 从队列中激活它。如果检索作业的状态为 ,则该作业处于活动状态。该过程将重复进行,直到作业队列 清除。PendingInProgress

有关更多信息,请参阅 Salesforce 开发人员限制和分配快速中的元数据限制 参考资料。

示例包 .xml 清单文件

本部分包括示例包 .xml 清单文件,这些文件演示如何工作 具有不同的元数据子集。清单文件可以包含多个<类型>元素 因此,如果需要,可以将各个示例合并到一个 Package.xml 清单文件中 在一个批处理中处理所有元数据。

列出了以下示例:

  • 标准 对象
  • 全部定制 对象
  • 标准选择列表 领域
  • 定制和标准 领域
  • 标准列表视图 对象
  • 安全 设置
  • 分配规则,自动响应 规则、升级规则
  • 共享规则
  • 托管组件 访问

有关清单文件结构的更多信息,请参阅使用 Zip 文件部署和检索元数据。

标准对象

此示例包 .xml 清单文件说明了如何工作 替换为标准 Account 对象。检索或部署标准对象包括 所有自定义字段和标准字段,但不可自定义的标准字段除外。 支持所有自定义字段。只有您可以自定义的标准字段是 支持,即可以添加帮助文本或启用的标准字段 历史记录跟踪或 Chatter 摘要跟踪。其他标准字段不是 支持,包括系统字段(如 CreatedById 或 LastModifiedDate)和自动编号字段。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Account</members>
        <name>CustomObject</name>
    </types>
    <version>59.0</version>
</Package>

请注意如何使用标准 Account 对象,方法是将其指定为 CustomObject 类型。但是,您不能使用星号通配符来处理所有 标准对象;每个标准对象必须按名称指定。

所有自定义对象

此示例包 .xml 清单文件说明了如何工作 替换为所有自定义对象。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>*</members>
        <name>CustomObject</name>
    </types>
    <version>59.0</version>
</Package>

此清单文件可用于检索或部署所有自定义对象,但不是全部 标准对象。

标准选择列表字段

在 API 版本 38.0 及更高版本中,StandardValueSet 类型表示标准 选择列表。选择列表不再像早期版本那样由字段表示。 此示例包 .xml 将行业标准选择列表表示为 StandardValueSet 类型。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Industry</members>
        <name>StandardValueSet</name>
    </types>
    <version>59.0</version>
</Package>

注意

标准值集的名称区分大小写。

行业标准值集对应于 API 版本 37.0 及更早版本中的 Account.Industry 或 Lead.Industry 字段。此示例显示 Account.Industry 选择列表的 package.xml 示例。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Account.Industry</members>
        <name>CustomField</name>
    </types>
    <version>37.0</version>
</Package>

注意

选择列表字段的名称区分大小写。

请注意字段中的语法,其中是对象的名称,例如 和 标准选择列表字段的名称,例如“行业”。objectName.picklistField<members>objectNameAccountpicklistField

下一个 package.xml 示例表示商机团队角色 在 API 版本 38.0 及更高版本中。将商机团队角色指定为 SalesTeamRole 标准值集。商机团队角色 具有与“客户团队”角色相同的选择列表值。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>SalesTeamRole</members>
        <name>StandardValueSet</name>
    </types>
    <version>59.0</version>
</Package>

SalesTeamRole 标准值集对应于以下值之一 API 版本 37.0 及更早版本中的字段名称:OpportunityTeamMember.TeamMemberRole、UserAccountTeamMember.TeamMemberRole、UserTeamMember.TeamMemberRole 和 AccountTeamMember.TeamMemberRole。 商机团队角色 在此示例包 .xml 中表示为 OpportunityTeamMember.TeamMemberRole 字段。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>OpportunityTeamMember.TeamMemberRole</members>
        <name>CustomField</name>
    </types>
    <version>37.0</version>
</Package>

了解标准值集的名称以及它们如何映射到选择列表字段 名称,请参阅 StandardValueSet 名称和标准选择列表字段。

自定义字段和标准字段

此示例包 .xml 清单文件说明了如何工作 在自定义和标准对象中使用自定义字段,在标准中使用标准字段 对象。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>MyCustomObject__c.MyCustomField__c</members>
        <name>CustomField</name>
    </types>
    <types>
        <members>Account.SLA__c</members>
        <members>Account.Phone</members>
        <name>CustomField</name>
    </types>
    <version>59.0</version>
</Package>

请注意 字段 where 是对象的名称(如 Account),是自定义或标准字段的名称,例如表示服务级别协议的 SLA 选择列表字段 选择。MyCustomObject 中的 MyCustomField 自定义域 自定义对象由其全名 唯一标识。同样,“客户标准”对象中的“电话标准”字段为 由其全名 .objectName.field<members>objectNamefieldMyCustomObject__c.MyCustomField__cAccount.Phone

支持所有自定义字段。只有您可以自定义的标准字段是 支持,即可以添加帮助文本或启用的标准字段 历史记录跟踪或 Chatter 摘要跟踪。其他标准字段不是 支持,包括系统字段(如 CreatedById 或 LastModifiedDate)和自动编号字段。

标准对象的列表视图

检索标准对象的列表视图的最简单方法是检索 对象。列表视图包含在检索到的组件中。请参阅部分 有关标准对象的本主题。

如果您不想检索所有 对象的详细信息。此示例包 .xml 清单文件 阐释如何使用标准 Account 对象的列表视图。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Account.AccountTeam</members>
        <name>ListView</name>
    </types>
    <version>59.0</version>
</Package>

请注意字段中的语法,其中是对象的名称,例如 Account,并且是 View Unique 列表视图的名称。如果检索此列表视图,则组件 存储在 objects/Account.object 中。objectName.listViewUniqueName<members>objectNamelistViewUniqueName

要检索包,请在调用 时在 RetrieveRequest 的 packageNames 字段中设置包的名称。包 .xml 清单文件 会自动填充到检索到的文件中。元素 在 package.xml 中包含检索到的包的名称。retrieve().zip<fullName>

如果在元素中使用星号通配符来检索特定元数据类型的所有组件,则检索到的 内容不包括托管包中的组件。<members>

有关托管包的详细信息,请参阅第二代托管包 打包开发人员指南。

检索托管包中的组件的最简单方法是检索 如前所述,通过在 的 packageNames 字段中设置包的名称来完成包。以下示例包.xml 清单文件演示了 检索包中的单个组件。RetrieveRequest

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>myns__MyCustomObject__c</members>
        <name>CustomObject</name>
    </types>
    <version>59.0</version>
</Package>

请注意字段中的 __ 语法,其中是包的命名空间前缀,是对象的名称。namespacePrefixobjectName<members>namespacePrefixobjectName命名空间前缀是 1 个字符到 15 个字符的字母数字标识符 将您的包及其内容与其他发布商的包区分开来 包。有关详细信息,请参阅创建和 为第二代托管注册命名空间 包。

安全设置

此示例包 .xml 清单文件说明了如何工作 使用组织的安全设置。在元素中指定 Security,在名称中指定 Settings 元素。<members>

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Security</members>
        <name>Settings</name>
    </types>
    <version>59.0</version>
</Package>

分配规则、自动响应规则、升级 规则

分配规则、自动响应规则和升级规则使用 用于访问规则集的不同包 .xml 类型名称或 对象类型的单个规则。例如,以下示例 package.xml 清单文件演示了如何访问 仅案例和潜在顾客的组织分配规则。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Case</members>
        <members>Lead</members>
        <name>AssignmentRules</name>
    </types>
    <version>59.0</version>
</Package>

以下示例包.xml 清单 文件说明了如何仅访问“samplerule” 案例分配规则 以及“newrule”潜在客户分配规则。请注意,类型名称是 and not 。

AssignmentRuleAssignmentRules

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Case.samplerule</members>
        <members>Lead.newrule</members>
        <name>AssignmentRule</name>
    </types>
    <version>59.0</version>
</Package>

同样,用于访问单个自动响应规则和 升级规则,使用 and 代替 和 。

AutoResponseRuleEscalationRuleAutoResponseRulesEscalationRules

共享规则

在 API 版本 33.0 及更高版本中,可以检索和部署所有共享规则 标准对象和自定义对象。此示例包 .xml 清单 文件说明了如何使用组织的共享规则,例如 检索潜在顾客对象的基于条件的特定共享规则,检索 所有对象的所有基于所有权的共享规则,并检索所有对象 Account 对象的基于区域的共享规则。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Lead.testShareRule</members> 
         <name>SharingCriteriaRule</name>
    </types>
    <types>
        <members>*</members> 
        <name>SharingOwnerRule</name>
    </types>
    <types>
        <members>Account.*</members>
        <name>SharingTerritoryRule</name>
    </types>
    <version>33.0</version>
</Package>

托管组件访问

在 API 版本 29.0 及更高版本中,您可以检索和 在配置文件中部署以下托管组件的访问设置,以及 权限集:

  • 顶点类
  • 应用程序
  • 自定义字段权限
  • 自定义对象权限
  • 自定义选项卡设置
  • 外部数据源
  • 记录类型
  • Visualforce 页面

在 API 版本 51.0 及更高版本中,您可以检索和部署登录的访问设置 流。

检索和部署托管组件权限时,请指定命名空间 后跟两个下划线。不支持通配符。

例如,假设您安装了一个命名空间为 MyNamespace 且自定义对象为 JobRequest__c 的托管包。将包中JobRequest__c的对象权限设置为自定义配置文件 MyProfile,您需要将以下内容添加到 .profile 文件中。

要部署,请执行以下操作:

<objectPermissions>
    <allowCreate>true</allowCreate>
    <allowDelete>true</allowDelete>
    <allowEdit>true</allowEdit>
    <allowRead>true</allowRead>
    <viewAllRecords>false</viewAllRecords>
    <modifyAllRecords>false</modifyAllRecords>
    <object>MyNamespace__JobRequest__c</object>
</objectPermissions>

要检索:

<types>
    <members>MyNamespace__JobRequest__c</members>
    <name>CustomObject</name>
</types>
<types>
    <members>MyProfile</members>
    <name>Profile</name>
</types>

检索权限集和配置文件时,请确保还检索任何 与权限和设置相关的组件。例如,当 检索应用程序可见性时,还必须检索关联的应用程序,以及何时检索 检索对象或字段权限时,还必须检索关联的 对象。

在部署中运行测试

生产环境中的默认测试执行

如果部署选项中未指定测试级别,则 默认测试执行行为取决于部署包的内容。什么时候 部署到生产环境后,除源自托管包的测试外,所有测试都是 如果您的部署包包含 Apex 类或触发器,则执行。如果您的包裹 不包含 Apex 组件,默认情况下不运行任何测试。

在 API 版本 33.0 及更早版本中,对以下组件运行了测试 所需的测试,例如自定义对象,而不仅仅是针对 Apex 组件。例如,如果 您的包包含一个自定义对象,所有测试都在 API 版本 33.0 及更早版本中运行。在 相比之下,从 API 版本 34.0 开始,不会对此包运行任何测试。API 版本 对应于 API 客户端的版本或您正在使用的工具的版本(Ant 迁移工具)。

您可以为非 Apex 组件的部署运行测试。您可以 通过在部署中设置测试级别来替代默认测试执行行为 选项。无论 部署包。我们建议您在开发中运行所有本地测试 环境,例如沙盒,然后再部署到生产环境。在 开发环境减少了在生产环境中运行所需的测试数量 部署。

API 版本 33.0 及更早版本的生产环境中的默认测试执行

若要部署到生产组织,将运行组织中的所有本地测试 默认情况下。默认情况下,源自已安装的托管包的测试不会运行。如果 任何测试失败,整个部署都将回滚。

如果部署包含以下元数据类型的组件,则所有本地测试都是 跑。

  • 顶点类
  • Apex组件
  • ApexPage(顶点页面)
  • Apex触发器
  • 文章类型
  • BaseSharingRule
  • CriteriaBasedSharingRule
  • 自定义字段
  • 自定义对象
  • 数据类别组
  • 已安装的软件包
  • NamedFilter(名称过滤器)
  • OwnerSharingRule
  • 权限集
  • 轮廓
  • 队列
  • 记录类型
  • RemoteSiteSetting
  • 角色
  • 共享原因
  • 领土
  • 验证规则
  • 工作流程

例如,不会对以下部署运行任何测试:

  • 1 CustomApplication 组件
  • 100 个报表组件和 40 个仪表板组件

但是,所有本地测试都针对以下任何示例部署运行,因为它们 包括上面列表中的至少一个组件:

  • 1 CustomField 组件
  • 1 个 ApexComponent 组件和 1 个 ApexClass 组件
  • 5 个 CustomField 组件和 1 个 ApexPage 组件
  • 100 个报表组件、40 个仪表板组件和 1 个 CustomField 组件

在部署中运行测试的子集

测试级别使你能够更好地控制在部署中运行哪些测试。自 缩短部署到生产的时间,在部署 Apex 组件时运行测试子集。这 生产环境中的默认测试执行行为也已更改。默认情况下,如果没有测试级别 指定时,不会执行任何测试,除非您的部署包包含 Apex 类或 触发器。

如果部署中某个 Apex 组件的代码覆盖率小于 75%,则部署 失败。如果指定的测试之一失败,则部署也会失败。我们建议您测试 首先在沙盒中部署,以确保指定的测试涵盖每个组件 充分。即使组织的整体代码覆盖率为 75% 或更高,个人 正在部署的 Apex 组件的覆盖范围可能较小。如果代码覆盖率要求 不满足,编写更多测试并将它们包含在部署中。

若要运行测试的子集,请设置测试 对象的级别。接下来,指定每个测试 要在 中运行的类。最后,作为参数传递给调用。以下示例执行这些步骤以仅运行指定的 测试类。RunSpecifiedTestsDeployOptionsDeployOptionsDeployOptionsdeploy()

// Create the DeployOptions object.
DeployOptions deployOptions = new DeployOptions();

// Set the appropriate test level.
deployOptions.setTestLevel(TestLevel.RunSpecifiedTests);

// Specify the test classes to run.
// String array contains test class names.
String[] tests = {"TestClass1", "TestClass2", "TestClass3"};
// Add the test class names array to the deployment options.
deployOptions.setRunTests(tests);

// Call deploy() by passing the deployment options object as an argument. 
AsyncResult asyncResult = metadatabinding.deploy(zipBytes,deployOptions);

有关运行特定测试的注意事项

  • 您只能指定测试类。不能指定单个测试方法。
  • 我们建议您重构测试类,以包含满足的最小测试数 代码覆盖率要求。重构测试类有助于缩短测试时间 执行时间,从而缩短部署时间。
  • 您可以通过使用非活动状态部署目标组织中的触发器来停用该触发器 州。但是,触发器必须以前已部署为活动状态。

在沙盒和生产部署中运行相同的测试

从 API 版本 34.0 开始,您可以选择在开发中运行哪些测试 环境,例如仅本地测试,以匹配在生产环境中运行的测试。在早期版本中, 如果在沙盒部署中启用了测试,则无法排除托管包 测试。

默认情况下,在部署到非生产组织(例如 沙盒或 Developer Edition 组织。指定要在开发中运行的测试 环境,设置 testLevel 部署 选项。例如,在部署中运行本地测试并排除托管包 tests,将对象上的 testLevel 设置为 。 接下来,将此对象作为参数传递给调用 如下。DeployOptionsTestLevel.RunLocalTestsdeploy()

// Create the DeployOptions object.
DeployOptions deployOptions = new DeployOptions();

// Set the appropriate test level.
deployOptions.setTestLevel(TestLevel.RunLocalTests);

// Call deploy() by passing the deployment options object as an argument. 
AsyncResult asyncResult = metadatabinding.deploy(zipBytes,deployOptions);

注意

无论 部署包的内容。相比之下,默认情况下,测试在生产环境中执行 仅当您的部署包包含 Apex 类或触发器时。可用于沙盒和生产部署。RunLocalTestsRunLocalTests

维护用户引用

在元数据部署期间保留用户字段。

当部署中的组件 指特定用户,例如工作流电子邮件的收件人 通知或仪表板运行用户,然后 Salesforce 尝试 通过比较在目标组织中查找匹配的用户 部署期间的用户名。

例如,当您将数据复制到沙盒时,包含来自生产组织的用户名的字段 更改为包含沙盒名称。在名为 的沙盒中,用户名变为 。当您部署 沙盒中的元数据,则用户名中的元数据将被忽略。testuser@acme.comuser@acme.com.testtest对于部署中的用户引用,Salesforce 执行 顺序如下:

  1. Salesforce 将源环境中的用户名与目标环境中的用户名进行比较 环境并调整组织域名。
  2. 如果两个或多个用户名匹配,Salesforce 会列出 匹配的名称和请求源环境中的一个用户 重命名。
  3. 如果源环境中的用户名不存在 目标环境,Salesforce 显示 错误,部署将停止,直到删除用户名 或解析为目标环境中的用户。

基于 CRUD 的元数据开发

使用基于 CRUD 的元数据调用创建、更新或删除设置和 组织或应用程序的配置组件。这些配置 组件包括自定义对象、自定义字段和其他配置元数据。这 元数据调用模仿 Salesforce 用户界面中用于创建、更新、 或删除组件。无论那里适用什么规则,也适用于这些调用。

元数据调用在这些方面与核心同步 API 调用不同。

  • 元数据 API 调用在单独的 WSDL 中可用。自 下载 WSDL,登录 Salesforce,从“设置”中,输入“快速查找”框,然后 选择 API,然后单击 Download Metadata WSDL 链接。API
  • 登录后,您必须将元数据 API 调用发送到元数据 API 端点。 其 URL 与 SOAP API 不同。从 返回的 LoginResult 中检索 metadataServerUrl SOAP API 调用。更多信息 关于 SOAP API,请参阅 SOAP API 开发人员 指南。login()
  • 元数据调用可以是同步的,也可以是异步的。CRUD 调用在 API 版本 30.0 及更高版本,与 API 核心调用类似,结果如下 在一次调用中返回。在早期 API 版本中,创建、更新和删除 只是异步的,这意味着结果不会立即返回 在一次通话中。
  • 有映射到相应核心 SOAP API 的同步元数据调用 同步调用。
    • createMetadata() 映射到 SOAP API 调用。create()
    • updateMetadata() 映射到 SOAP API 调用。update()
    • deleteMetadata() 映射到 SOAP API 调用。delete()

注意

元数据 API 还支持和调用检索和部署元数据 组件。有关更多信息,请参阅部署和检索元数据。retrieve()deploy()

使用同步调用进行基于 CRUD 的开发 Java 示例

本节将指导您完成使用基于 CRUD 的示例 Java 客户端应用程序 调用。此示例应用程序执行以下主要任务。

  1. 使用该类 创建元数据连接。有关更多信息,请参阅步骤 3:演练 Java 示例代码。MetadataLoginUtil.java
  2. 调用 createMetadata() 以创建自定义 对象。此调用在一次调用中返回结果。
  3. 将返回的对象检查到 检查操作是否成功,如果没有成功,则写入组件名称, 错误消息和状态代码。SaveResult
import com.sforce.soap.metadata.*;

/**
 * Sample that logs in and creates a custom object through the metadata API
 */
public class CRUDSampleCreate {
    private MetadataConnection metadataConnection;

    // one second in milliseconds
    private static final long ONE_SECOND = 1000;

    public CRUDSampleCreate() {
    }

    public static void main(String[] args) throws Exception {
        CRUDSampleCreate crudSample = new CRUDSampleCreate();
        crudSample.runCreate();
    }

    /**
     * Create a custom object. This method demonstrates usage of the
     * create() and checkStatus() calls.
     *
     * @param uniqueName Custom object name should be unique.
     */
    private void createCustomObjectSync(final String uniqueName) throws Exception {
        final String label = "My Custom Object";
        CustomObject co = new CustomObject();
        co.setFullName(uniqueName);
        co.setDeploymentStatus(DeploymentStatus.Deployed);
        co.setDescription("Created by the Metadata API Sample");
        co.setEnableActivities(true);
        co.setLabel(label);
        co.setPluralLabel(label + "s");
        co.setSharingModel(SharingModel.ReadWrite);

        // The name field appears in page layouts, related lists, and elsewhere.
        CustomField nf = new CustomField();
        nf.setType(FieldType.Text);
        nf.setDescription("The custom object identifier on page layouts, related lists etc");
        nf.setLabel(label);
        nf.setFullName(uniqueName);
        customObject.setNameField(nf);

        SaveResult[] results = metadataConnection
                .createMetadata(new Metadata[] { co });

        for (SaveResult r : results) {
            if (r.isSuccess()) {
                System.out.println("Created component: " + r.getFullName());
            } else {
                System.out
                        .println("Errors were encountered while creating "
                                + r.getFullName());
                for (Error e : r.getErrors()) {
                    System.out.println("Error message: " + e.getMessage());
                    System.out.println("Status code: " + e.getStatusCode());
                }
            }
        }
    }

    private void runCreate() throws Exception {
        metadataConnection = MetadataLoginUtil.login();
        // Custom objects and fields must have __c suffix in the full name.
        final String uniqueObjectName = "MyCustomObject__c";
        createCustomObjectSync(uniqueObjectName);
    }
}

使用异步调用进行基于 CRUD 的开发的 Java 示例

重要

本节中的示例取决于异步 CRUD 调用。异步 CRUD 调用是 no 从 API 版本 31.0 开始提供更长的时间,并且仅在早期 API 中可用 版本。create()

本节将指导您完成一个示例 Java 客户端应用程序,该应用程序使用 基于 CRUD 的异步调用。此示例应用程序执行以下主要操作 任务:

  1. 使用该类 创建元数据连接。有关更多信息,请参阅步骤 3:演练 Java 示例代码。MetadataLoginUtil.java
  2. 调用 create() 来创建一个 自定义对象。Salesforce 返回 的 AsyncResult 对象 您尝试创建的每个组件。AsyncResult 对象是 当操作从队列移动到 “已完成”或“错误”状态。
  3. 在循环中调用 checkStatus() 直到 AsyncResult 中的 status 值指示创建操作已完成。

请注意每个 API 调用后面的错误处理代码。

import com.sforce.soap.metadata.*;

/**
 * Sample that logs in and creates a custom object through the metadata api
 */
public class CRUDSample {
    private MetadataConnection metadataConnection;

    // one second in milliseconds
    private static final long ONE_SECOND = 1000;

    public CRUDSample() {
    }

    public static void main(String[] args) throws Exception {
        CRUDSample crudSample = new CRUDSample();
        crudSample.runCreate();
    }

    /**
     * Create a custom object. This method demonstrates usage of the
     * create() and checkStatus() calls.
     *
     * @param uniqueName Custom object name should be unique.
     */
    private void createCustomObject(final String uniqueName) throws Exception {
        final String label = "My Custom Object";
        CustomObject customObject = new CustomObject();
        customObject.setFullName(uniqueName);
        customObject.setDeploymentStatus(DeploymentStatus.Deployed);
        customObject.setDescription("Created by the Metadata API Sample");
        customObject.setLabel(label);
        customObject.setPluralLabel(label + "s");
        customObject.setSharingModel(SharingModel.ReadWrite);

        // The name field appears in page layouts, related lists, and elsewhere.
        CustomField nf = new CustomField();
        nf.setType(FieldType.Text);
        nf.setDescription("The custom object identifier on page layouts, related lists etc");
        nf.setLabel(label);
        nf.setFullName(uniqueName);
        customObject.setNameField(nf);

        AsyncResult[] asyncResults = metadataConnection.create(
            new CustomObject[]{customObject});
        if (asyncResults == null) {
            System.out.println("The object was not created successfully");
            return;
        }

        long waitTimeMilliSecs = ONE_SECOND;

        // After the create() call completes, we must poll the results of the checkStatus()
        // call until it indicates that the create operation has completed.
        do {
            printAsyncResultStatus(asyncResults);
            waitTimeMilliSecs *= 2;
            Thread.sleep(waitTimeMilliSecs);
            asyncResults = metadataConnection.checkStatus(new String[]{asyncResults[0].getId()});
        } while (!asyncResults[0].isDone());

        printAsyncResultStatus(asyncResults);
    }

    private void printAsyncResultStatus(AsyncResult[] asyncResults) throws Exception {
        if (asyncResults == null || asyncResults.length == 0 || asyncResults[0] == null) {
            throw new Exception("The object status cannot be retrieved");
        }

        AsyncResult asyncResult = asyncResults[0]; //we are creating only 1 metadata object

        if (asyncResult.getStatusCode() != null) {
            System.out.println("Error status code: " +
                    asyncResult.getStatusCode());
            System.out.println("Error message: " + asyncResult.getMessage());
        }

        System.out.println("Object with id:" + asyncResult.getId() + " is " +
            asyncResult.getState());
    }

    private void runCreate() throws Exception {
        metadataConnection = MetadataLoginUtil.login();
        // Custom objects and fields must have __c suffix in the full name.
        final String uniqueObjectName = "MyCustomObject__c";
        createCustomObject(uniqueObjectName);
    }
}

REST 资源

使用 REST 资源进行移动 Salesforce 组织与本地文件系统之间的元数据(XML 文件)。

deployRequest

XML 文件中的数据使用英语(美国)区域设置进行格式设置。这种方法 确保对依赖于区域设置的字段(如日期字段)的解释一致 在使用不同语言的组织之间进行数据迁移期间。组织可以 支持多种语言向用户展示。

元数据部署主要用于以下开发方案。

  • 在沙盒组织中开发自定义应用程序(或自定义)。后 完成开发和测试,然后部署应用程序或自定义项 使用元数据 API 进入生产组织。
  • 在 Developer Edition 组织中对应用程序进行团队开发。开发后 并完成测试,然后您可以通过 Lightning Platform 分发应用程序 AppExchange。

使用 Zip 文件

该资源用于部署 .zip 文件。在 .zip 文件中有一个项目清单 (package.xml) 列出要检索或部署的内容,以及组织到其中的一个或多个 XML 组件 文件夹。deployRequest

注意

组件是 元数据类型。 例如,是自定义对象的元数据类型,组件是 自定义对象的实例。CustomObjectMyCustomObject__c

部署在 .zip 文件中的文件可以解压缩 驻留在组织中的组件(例如标准对象)。这 文件也可以是驻留在命名包中的打包组件。

注意

一次最多可以部署 10,000 个文件。(在 API 版本 43.0 中 及更高版本,AppExchange 软件包最多可以包含 12,500 个文件。.zip 文件大小限制 适用于 SOAP 调用的不适用于 REST 资源。但是,400 MB 的组件限制 上传后解压缩到解压缩的文件夹中适用于 SOAP 和 REST 部署。deployRequest

每个 .zip 文件都包含一个项目清单、一个名为 package.xml 的文件以及一组包含组件的目录。 清单文件定义您尝试检索或部署的组件,以及 用于部署或检索的 API 版本。

下面是一个示例包 .xml 文件。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>MyCustomObject__c</members>
        <name>CustomObject</name>
    </types>
    <types>
        <members>*</members>
        <name>CustomTab</name>
    </types>
    <types>
        <members>Standard</members>
        <name>Profile</name>
    </types>
    <version>59.0</version>
</Package>

可以在 package.xml 中定义以下元素。

  • <fullName>包含服务器端的名称 包。如果不存在,则它是客户端包。<fullName>unpackaged
  • <types>包含元数据类型的名称(对于 示例, ) 和命名成员 (对于 示例,) 进行部署。您可以在清单中添加多个元素 文件。CustomObjectmyCustomObject__c<types>
  • <members>包含组件的 fullName,例如 。对于许多元数据类型,可以将 中的值替换为通配符(星号),而不是单独列出每个成员。为 允许通配符的元数据类型列表,请参阅“允许 “元数据类型”中的通配符 (*)?“ 列。MyCustomObject__cmembers*注意在元素中指定 Security,在名称中指定 Settings 元素。<members>
  • <name>包含元数据类型,例如 或 。目录中的每种元数据类型都定义了一个名称。任何 扩展元数据的元数据类型是 有效值。输入的名称必须与 元数据 API WSDL。有关列表,请参阅元数据类型。CustomObjectProfile
  • <version>是使用的 API 版本号 部署或检索 .zip 文件时。目前 有效值为 。59.0

有关演示如何工作的更多示例包 .xml 清单文件 使用不同的元数据子集,请参阅示例包 .xml 清单 文件。

要删除组件,请参阅从组织中删除组件。

使用 REST 通过 Apex 测试部署元数据

使用 REST 资源部署到 启动处理部署的所有操作的请求。

deployRequest

您可以部署或 一次最多可检索 10,000 个文件。AppExchange 软件包使用不同的限制: 最多可包含 35,000 个文件。已部署或检索的 .zip 的最大大小 文件大小为 39 MB。如果文件在解压缩的文件夹中解压缩,则大小限制 是 400 MB。URIhttps://host/services/data/vXX.0/metadata/deployRequest格式JSON格式HTTP 方法发布认证Authorization: Bearer token

deployOptions 参数

注意

查看可以节省时间的部署和方法的默认测试行为 同时仍使您能够满足测试要求,请参阅在部署中运行测试和在沙盒和生产部署中运行相同的测试。

参数描述
allowMissingFiles布尔。如果 package.xml 中指定的文件不是 在 .zip 文件中,指定部署是否仍可 成功。不要为部署到生产组织设置此参数。
autoUpdatePackage保留以备将来使用。
仅检查布尔。缺省值为 .设置为执行 组件,而不将组件保存在目标组织中。验证使您能够 验证将在部署中生成但未生成的测试结果 提交任何更改。验证完成并通过测试后,它就可以符合条件 用于在不重新运行测试的情况下进行部署。请参阅在不进行测试的情况下部署最近验证的组件集。falsetrue
ignore警告布尔。指示部署是否 尽管有一个或多个警告 () 或不 (),但允许成功完成。缺省值为 .truefalsefalse的 DeployMessage 对象 warning 包含以下值:问题类型—Warningproblem – 警告的文本。如果出现警告,并且 ignoreWarnings 设置为 ,则 success 字段 在 DeployMessage 中是 。如果 ignoreWarnings 设置为 ,则 success 设置为 ,并且警告被视为 错误。truetruefalsefalse
执行检索保留以备将来使用。
purgeOnDelete(清除OnDelete)布尔。如果 ,destructiveChanges.xml 清单文件中已删除的组件不会存储在 回收站。相反,它们会立即符合删除条件。true此选项仅适用于 Developer Edition 或沙盒组织。 它在生产组织中不起作用。
rollbackOnError布尔。指示任何故障是否会导致完全回滚 () 或 ()。如果,无论什么动作都可以 在不执行错误的情况下执行,并返回其余的错误 行动。此参数必须设置为 if 您正在部署到生产组织。缺省值为 。truefalsefalsetruefalse
运行测试字符串 []。要在部署期间运行的 Apex 测试列表。指定类 name,每个实例一个名称。类名还可以指定带有点的命名空间 表示法。有关更多信息,请参见在部署中运行测试的子集。要使用此选项,请将 testLevel 设置为 。RunSpecifiedTests
单包布尔。指示指定的文件是指向具有单个包 () 的目录结构,还是指向一组包 (..ziptruefalse)
testLevelTestLevel(字符串类型的枚举)。自选。指定运行哪些测试 作为部署的一部分。无论类型如何,都会强制执行测试级别 部署包中存在的组件。有效值为:NoTestRun– 不运行任何测试。这 测试级别仅适用于开发环境的部署,例如 沙盒、Developer Edition 或试用组织。此测试级别是 开发环境的默认值。RunSpecifiedTests– 仅运行您在 runTests 选项中指定的测试。代码覆盖率要求 使用此测试级别时,与默认覆盖率要求不同。每 部署包中的类和触发器必须被执行的 测试至少 75% 的代码覆盖率。此覆盖范围是针对每个 类和触发器单独,与整体覆盖范围不同 百分比。RunLocalTests—所有测试在您的 org 运行,但源自 installed managed 和 解锁的包裹。此测试级别是生产环境的默认级别 包含 Apex 类或触发器的部署。RunAllTestsInOrg—所有测试都是 跑。这些测试包括组织中的所有测试,包括托管测试 包。如果未指定测试级别,则默认测试执行行为为 使用。请参阅在部署中运行测试。作为部署的一部分运行的 Apex 测试始终以同步和串行方式运行。

请求正文:部署元数据

部署元数据时,请求包括部署参数和 .zip 包含组件目录和清单的文件。将标头设置为 ,Content-Type: multipart/form-data

此示例 POST 请求创建一个启动部署的对象。deployRequest

  1. POST 请求标头设置为并定义一个值来封装请求的不同子部分。Content-Type: multipart/form-databoundary
  2. 在第一个边界之后的子部分中,JSON 请求创建一个子对象,用于传递部署参数。deployOptions
  3. 第二个边界后面的子部分指定包含清单的 .zip 文件 和组件目录。
POST /services/data/v48.0/metadata/deployRequest
Authorization: Bearer 00D....
Content-Type: multipart/form-data; boundary=--------------------------BOUNDARY
----------------------------BOUNDARY
Content-Disposition: form-data; name="json"
Content-Type: application/json
{ 
    "deployOptions" :
        {
        "allowMissingFiles" : false,
        "autoUpdatePackage" : false,
        "checkOnly" : false,
        "ignoreWarnings" : false,
        "performRetrieve" : false,
        "purgeOnDelete" : false,
        "rollbackOnError" : false,
        "runTests" : null,
        "singlePackage" : true,
        "testLevel" : "RunAllTestsInOrg"
        }
    }
----------------------------BOUNDARY
Content-Disposition: form-data; name="file"; filename="deploy.zip"
Content-Type: application/zip

//Contents of deploy.zip
----------------------------BOUNDARY--

响应正文:部署元数据

当返回 HTTP 状态码 (Created) 时,您的 请求已成功,并导致创建正在处理的部署。201

{ "id" : "0Afxx00000001VPCAY",
  "deployOptions" : 
   { "checkOnly" : false,
     "singlePackage" : false,
     "allowMissingFiles" : false,
     "performRetrieve" : false,
     "autoUpdatePackage" : false,
     "rollbackOnError" : true,
     "ignoreWarnings" : false,
     "purgeOnDelete" : false,
     "runAllTests" : false },
  "deployResult" : 
   { "id" : "0Afxx00000001VPCAY",
     "success" : false,
     "checkOnly" : false,
     "ignoreWarnings" : false,
     "rollbackOnError" : true,
     "status" : "Pending",
     "runTestsEnabled" : false,
     "done" : false } }

deployResult 参数

参数描述
编号ID。正在部署的组件的 ID。
canceled作者ID。取消部署的用户的 ID。
canceledByName字符串。取消部署的用户的全名。
仅检查布尔。指示此部署是否用于检查 在不更改组织的情况下部署的文件 () 或不 ()。仅检查部署不会 部署任何组件或以任何方式更改组织。truefalse
完成日期日期时间。部署过程结束的时间戳。
创建者ID。创建部署的用户的 ID。
createdByName字符串。创建部署的用户的全名。
创建日期日期时间。收到部署请求时的时间戳。
DeployDetails。提供正在进行的部署的详细信息,或者 ended if 添加为 查询到 GET 请求。?includeDetails=true
布尔。指示服务器是否完成了部署请求的处理 对于指定的 ID。
errorMessage (错误消息)字符串。与 errorStatusCode 字段中的值(如果有)相对应的消息。
errorStatusCode字符串。如果在部署请求期间发生错误,则状态代码为 返回,在 errorMessage字段中返回状态码对应的消息。
ignore警告布尔。自选。缺省值为 . 指定即使部署生成警告,部署是否继续。 不要将此参数设置为 for deployments 到生产组织。falsetrue
上一个修改日期日期时间。部署过程上次更新的时间戳。
numberComponentErrors国际部署过程中部署的组件数。使用这个 value 替换为 numberComponentsTotal 值来获取估计值 部署进度。
numberComponentsTotal(数量组件合计)国际部署中的组件总数。将此值与 numberComponentsDeployed 值一起使用可获取 部署进度。
numberTestErrors国际在此期间生成错误的 Apex 测试数 部署。
numberTests已完成此部署已完成的 Apex 测试数。将此值与 numberTestsTotal 值一起使用,可获取部署的 测试进度。
numberTestsTotal国际此部署的 Apex 测试总数。将此值用于 numberTestsCompleted 值来获取 部署的测试进度。此字段中的值在 部署已开始对正在部署的组件运行测试。
runTests已启用布尔。指示 Apex 测试是否作为此部署的一部分运行 () 或不 ()。测试要么作为部署的一部分自动运行,要么 可以设置为在 deployOptions 子对象中运行。truefalse
rollbackOnError布尔。缺省值为 .表明 是否有任何故障导致完全回滚 () 或不 ()。如果 ,可以执行任何一组操作 不执行任何错误,其余操作返回错误。 如果 部署到生产组织。truetruefalsefalsetrue
开始日期日期时间。部署过程开始的时间戳。
状态详细信息字符串。指示正在部署哪个组件或哪个 Apex 测试类 运行。
地位指示部署的当前状态。有效值为:PendingInProgressSucceededSucceededPartialFailedCancelingCanceled
成功布尔。指示部署是否成功 () 或 ()。truefalse

使用 REST 资源检查部署状态

检查部署状态,方法是在 URL 响应正文与原始部署请求返回的响应正文类似,但它 包括有关正在进行的部署的信息。URIhttps://host/services/data/vXX.0/metadata/deployRequest/deployRequestId

自 在响应中包含更多详细信息,请使用:

https://host/services/data/vXX.0/metadata/deployRequest/deployRequestId?includeDetails=true格式JSON格式HTTP 方法获取认证Authorization: Bearer token

响应正文:部署元数据

以下示例显示了作为查询添加到 GET 请求时的响应。?includeDetails=true

{ 
	       "id" : "0Afxx00000000lWCAQ"
	       "url" : "https://host/services/data/vXX.0/metadata/deployRequest/0Afxx00000000lWCAQ?includeDetails=true",
        "deployResult" :
            {
            "checkOnly" : "false",
            "ignoreWarnings" : "false",
            "rollbackOnError" : "false",
            
            "status : "InProgress",
            "numberComponentsDeployed" : "10", 
            "numberComponentsTotal" : "1032",
            "numberComponentErrors" : "0",
            "numberTestsCompleted" : "45",
            "numberTestsTotal" : "135",
            "numberTestErrors" : "0",            
            "details" :  { 
	             "componentFailures" : [],
             	"componentSuccesses" : [],
                    "retrieveResult" : null,
                    "runTestResults" : {
                    "numRun" : 0,
                    "successes" : [ … ],
                    "failures" : []
	         	         }
            },

            "createdDate" : "2017-10-10T08:22Z",       
            "startDate" : "2017-10-10T08:22Z",
            "lastModifiedDate" : "2017-10-10T08:44Z",
            "completedDate" : "2017-10-10T08:44Z",

            "errorStatusCode" : null,
            "errorMessage" : null,
            "stateDetail" : "Processing Type: Apex Component",

            "createdBy" : "005xx0000001Sv1m",
            "createdByName" : "stephanie stevens", 
            "canceledBy" : null,
            "canceledByName" : null,            
            "isRunTestsEnabled" : null
            }

       "deployOptions": {    
             "allowMissingFiles" : false,
             "autoUpdatePackage" : false,
             "checkOnly" : true,
             "ignoreWarnings" : false,
             "performRetrieve" : false,
             "purgeOnDelete" : false,
             "rollbackOnError" : false,
             "runTests" : null,
             "singlePackage" : true,
             "testLevel" : "RunAllTestsInOrg"
             }
     }

预计返回 HTTP 状态代码 (OK)。200

部署最近验证的组件集,无需测试

您可以通过跳过 Apex 的执行,在更短的时间内将组件部署到生产环境 在已经满足测试要求时进行测试。

  • 在过去 10 年中,这些组件已针对目标环境成功验证 日。
  • 作为验证的一部分,目标组织中的 Apex 测试已通过。
  • 满足代码覆盖率要求。
    • 如果运行组织中的所有测试或所有本地测试,则总体代码覆盖率至少为 75%, 和 Apex 触发器有一定的覆盖范围。
    • 如果使用测试级别运行特定测试,则至少覆盖了 75% 的要部署的每个类和触发器 单独。RunSpecifiedTests

此操作等效于在 Salesforce 用户界面中的“部署状态”页面。

要在使用资源时验证但不部署一组组件,请将参数设置为 。记下响应中的部署请求 ID。使用此 ID (与成功的验证相关联)以部署组件集,而无需重复 验证。deployRequestcheckOnlydeployOptionstrueURIhttps://host/services/data/vXX.0/metadata/deployRequest/validatedDeployRequestId格式JSON格式HTTP 方法发布认证Authorization: Bearer token

请求正文:部署最近验证的组件集,无需测试

注意

用于部署最近验证的组件集的 HTTP 方法是 POST,而不是 PATCH。用 PATCH 将创建一个新部署。

{ 
       "validatedDeployRequestId" : "0Afxx00000000lWCAQ"
    }

如果没有满足验证要求的相应部署包,则 接收 HTTP 状态代码 (Not Found)。如果已验证 找到部署包,返回的 HTTP 状态码为 (Created)。404201

响应正文:部署最近验证的组件集,而不进行测试

注意

来自未验证请求的部署的响应正文包括新的请求 ID。 因为它与之前对仅验证部署的请求是分开的。

{ 
       "validatedDeployRequestId" : "0Afxx00000000lWCAQ"
       "id" : "0Afxx00000000lWMEM"
       "url" : "https://host/services/data/vXX.0/metadata/deployRequest/0Afxx00000000lWMEM",
       "deployOptions" :
             {
             "allowMissingFiles" : false,
             "autoUpdatePackage" : false,
             "checkOnly" : true,
             "ignoreWarnings" : false,
             "performRetrieve" : false,
             "purgeOnDelete" : false,
             "rollbackOnError" : false,
             "runTests" : null,
             "singlePackage" : true,
             "testLevel" : "RunAllTestsInOrg"
}
     }

当返回 HTTP 状态码 (Created) 时,您的 请求已成功,并导致创建正在处理的部署。在 在前面的示例响应正文中,仅验证部署请求的 ID 为 ;部署的 ID,不带 验证请求是 。2010Afxx00000000lWCAQ0Afxx00000000lWMEM

使用 REST 取消正在进行的部署

您可以请求取消正在进行的部署。使 通过修补正在进行的 .取消是异步处理的。

deployRequestURIhttps://host/services/data/vXX.0/metadata/deployRequest/deployRequestId格式JSON格式HTTP 方法补丁认证Authorization: Bearer token

请求正文:请求部署取消

部署取消的 JSON 请求正文包括 源语言。deployRequest

{ 
     "deployResult":
           {
           "status" : "Canceling"
           }
    }

响应正文:请求部署取消

由于取消请求是异步处理的,因此响应中显示的状态 body 可以是 或 。CancelingCanceled

{ 
      	"id" : "0Afxx00000000lWCAQ"
      	"url" : “https://host/services/data/vXX.0/metadata/deployRequest/0Afxx00000000lWCAQ",
       "deployResult":    
             {
             "checkOnly" : "false",
             "ignoreWarnings" : "false",
             "rollbackOnError" : "false",             
             "status : "Canceling",  // or Canceled
             "numberComponentsDeployed" : "10",
             "numberComponentsTotal" : "1032",
             "numberComponentErrors" : "0",
             "numberTestsCompleted" : "45",
             "numberTestsTotal" : "135",
             "numberTestErrors" : "0",
             "details" :  { 
                "componentFailures" : [],
                "componentSuccesses" : [],
                      "retrieveResult" : null,
                      "runTestResults” : {
                         "numRun" : 0,
                         "successes" : [ … ],
                         "failures" : []
                 	   	}             
                },

                "createdDate" : "2017-10-10T08:22Z",
                "startDate" : "2017-10-10T08:22Z",
                "lastModifiedDate" : "2017-10-10T08:44Z",
                "completedDate" : "2017-10-10T08:44Z",
                "errorStatusCode" : null,
                "errorMessage" : null,
                "stateDetail" : "Processing Type: Apex Component",
                "createdBy" : "005xx0000001Sv1m",
                "createdByName" : "steve stevens",
                "canceledBy" : null, 
                "canceledByName" : null,
                "isRunTestsEnabled" : null
                }
       }

当返回 HTTP 状态码 (Accepted) 时,您的 取消请求正在进行中或成功。202

在 Salesforce CLI 中使用 REST API 部署元数据

默认情况下,Salesforce CLI 命令使用元数据 SOAP API 将源部署到您的组织。您可以 通过设置 CLI 配置值或环境来改用元数据 REST API 变量。与 SOAP API 相比,REST API 提供了更快的部署速度。

project deploy start

用户 所需权限
要从 Salesforce CLI 使用元数据 API,请执行以下操作:通过元数据 API 函数修改元数据或修改所有数据

使用 Salesforce CLI 运行时配置变量或环境变量,用于将 REST API 设置为 默认值。有关更多信息,请参阅《Salesforce DX 设置指南》。org-metadata-rest-deploySF_ORG_METADATA_REST_DEPLOY

此示例使用配置值来设置当前 项目:

sf config set org-metadata-rest-deploy true

若要为所有项目全局设置默认值,请使用以下标志:–global

sf config set org-metadata-rest-deploy true --global

注意

仅部署源的命令,例如 project deploy start,支持 REST API。检索源的命令(如项目检索启动)始终使用 SOAP API。

以下是部署限制。

特征限制
最大压缩 .zip 文件夹大小1(SOAP API)约39MB
最大未压缩文件夹大小2(SOAP API)约400MB
AppExchange 软件包(REST 和 SOAP)中的最大文件数 API接口)30,000(API 版本 47.0 及更高版本)22,000(API 版本 46.0)17,500(API 版本 45.0)12,500(API 版本 43.0 和 44.0)10,000(API 版本 42.0 及更早版本)
包中的最大文件数(REST 和 SOAP API)10,000

1元数据 API base-64 在压缩组件后对其进行编码。这 生成的 .zip 文件不能超过 50 MB。Base-64 编码增加了 有效负载大约减少 22%,因此压缩的有效负载不能超过大约 编码前为 39 MB。

2使用 Ant 迁移工具部署解压项目时,所有 首先压缩项目中的文件。未压缩的最大大小 未压缩项目中的组件为 400 MB 或更少,具体取决于文件的 压缩比。如果文件的压缩率较高,则可以将 总计约为 400 MB,因为压缩大小将小于 39 MB。 但是,如果组件不能被压缩太多,比如二进制静态资源, 您可以迁移小于 400 MB。

错误处理

元数据 API 调用返回客户端应用程序的错误信息 可用于识别和解决运行时错误。

元数据 API 提供这些类型的错误处理。

  • 由于元数据 API 使用企业或合作伙伴 WSDL 进行身份验证,因此它使用 SOAP 错误 在这些 WSDL 中为格式不正确的消息导致的错误定义的消息, 身份验证失败或类似问题。每个 SOAP 错误都有一个关联的 ExceptionCode。有关详细信息,请参阅《SOAP API 开发人员指南》中的错误处理。
  • 对于异步 create()、update() 和 delete() 调用的错误, 请参阅关联组件的 AsyncResult 对象的 statusCode 字段中的错误状态代码。
  • 有关同步 CRUD 调用的错误,请参阅 Error 对象的 statusCode 字段中的错误状态代码 对应于相应结果对象的 errors 字段返回的数组中的每个错误。例如,createMetadata() 的结果对象是 SaveResult。
  • 对于 deploy() 的错误,请参阅 关联的 DeployMessage 对象中的问题和成功字段 元件。
  • 对于 retrieve() 的错误, 请参阅 RetrieveMessage 对象中的问题字段 对于关联的组件。

有关示例代码,请参阅步骤 3:演练 Java 示例代码。

会话过期的错误处理

当您通过通话登录时,新的客户端会话 开始,并生成相应的唯一会话 ID。会话在以下时间后自动过期 在 Salesforce 应用程序的“Security Controls”设置区域中指定的时间量(默认为 2 小时)。 会话过期时,将返回异常代码INVALID_SESSION_ID。如果发生这种情况, 您必须再次调用该调用。更多信息 关于,请参阅《SOAP API 开发人员指南》。login()login()login()

快速入门:元数据 API

面向初学者开发人员的资源

如果您是初学者开发人员,并且以前没有使用过 Salesforce CLI,请了解如何设置 您的环境和示例应用程序的实践。这些 Trailhead 将引导您完成 使用 SFDX 进行设置,并向您介绍元数据 API。

使用 Salesforce CLI 和源代码管理开发应用程序 登山口

演练使用以下方法设置环境和使用 Salesforce CLI 进行开发 Dreamhouse 示例应用。向 Dreamhouse 应用程序添加功能后,将元数据部署到 使用 Salesforce CLI 的 Dev Hub 组织。

包 .xml 元数据管理

详细了解元数据和包.xml文件。生成包.xml文件以部署更改 从零开始的组织到您的 Trailhead Playground。

使用元数据 API 进行开发的快速入门

如果您在 Salesforce 开发方面有一些经验,但想开始使用元数据 API,请使用此快速入门。本快速入门将引导您完成元数据的检索 组件,这是开发过程的第一步。

  1. 先决条件 在开始使用元数据 API 进行开发之前
    ,请完成这些先决条件。
  2. 步骤 1:(可选)使用 UI
    将元数据组件添加到组织 如果您从没有自定义项的新实践组织开始,则只有无法检索的标准元数据。要使用元数据 API 检索调用,请在 Salesforce UI 上将组件添加到您的实践组织。如果您正在处理现有项目,则已经有要检索的组件,可以跳过此步骤。
  3. 步骤 2:生成包 .xml 清单
    package.xml 清单文件列出了要从组织中检索的组件。
  4. 步骤 3:使用元数据 API
    检索组件 使用 Salesforce CLI,检索包.xml 清单中指定组件的文件表示形式。

先决条件

在开始使用元数据 API 进行开发之前,请完成这些先决条件。

  • 要通过命令行访问元数据 API,请安装 Salesforce CLI。
  • 若要创建开发环境,请注册 适用于 Salesforce 开发人员版。Developer Edition 组织是一个免费开发项目 用于独立于生产数据构建和测试解决方案的环境。
  • 安装适用于 Visual Studio Code 的 Salesforce 扩展。 这些工具提供了与开发组织(临时组织、沙箱、 和 DE orgs)、Apex、Aura 组件和 Visualforce。
  • 确认您拥有“已启用 API”权限,并通过元数据 API 修改元数据 “函数”权限或“修改所有数据”权限。如果您没有这些权限 设置、修改元数据权限。
  • 在组织中启用 Dev Hub。Dev Hub 允许你 创建和管理临时组织,以便在不影响生产数据的情况下进行开发 或元数据。
  • 要允许访问受保护的资源(如生产数据和元数据),请授权您的组织。
  • 在组织中启用 Dev Hub。Dev Hub 允许你 创建和管理临时组织,以便在不影响生产数据的情况下进行开发 或元数据。

步骤 1:(可选) 使用 UI 将元数据组件添加到组织

如果您从没有自定义项的新实践组织开始,则只需 具有无法检索的标准元数据。要使用 Metadata API 检索调用,请添加 组件添加到您的实践组织。如果您正在处理现有项目,则 已有要检索的组件,可以跳过此步骤。

  1. 在“设置”中,单击“创建”。
  2. 选择“自定义对象”。
  3. “标签”(Label) 和“复数标签”(Plural Label) 输入任意名称。
  4. 保存组件。

步骤 2:生成包.xml 清单

package.xml 清单文件列出了要从 组织。

Package.xml 清单结构

package.xml 清单使用可扩展标记语言 (XML) 来标识和迁移 元数据组件。package.xml 清单的基本框架是用元素构建的。元素指定元数据类型 你想与之合作。您可以将多个添加到包.xml文件。<types><types><types>

元素内部是元素和元素。元素 选择特定类型的单个组件,<name> 元素选择 元数据组件类型。要使用特定组件,请在元素中输入该组件的 。<types><name><members><members>fullName<members>

例如,若要检索 Account 组件,请在 package.xml 的元素中添加 Account,并在元素中添加 CustomObject。当您发出检索调用时,您将检索 仅来自您组织的 Account 组件。<members><name>

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Account</members>
        <name>CustomObject</name>
    </types>
    <version>59.0</version>
</Package>

检索自定义对象

若要检索元数据类型的所有组件,请不要指定组件的组件。请改用通配符 *(星号)。一些 组件(如标准对象)不支持 *(星号)作为说明符。fullName<members>

要从您的组织中检索所有自定义对象,请执行以下操作:

  1. (可选)如果您没有项目文件夹,请使用 Salesforce CLI 创建一个 用于组织项目的新目录。使用指定的 楼盘名称:sf project generate –name YourProjectName
  2. 在项目中创建名为 package.xml 的文件。
  3. 在文本编辑器中,打开文件并粘贴以下脚本:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>*</members>
        <name>CustomObject</name>
    </types>
    <version>59.0</version>
</Package>

现在您有一个 package.xml 文件,我们可以使用它来检索所有自定义对象。什么时候 您可以自己开发更多组件,您可以使用以下命令从您的组织中检索更多组件 多个元素。<types>

步骤 3:使用元数据 API 检索组件

使用 Salesforce CLI,检索 包 .xml 清单。

元数据 API 检索的两个选项

您可以使用以下两个命令之一来检索元数据组件。

  1. 若要检索 package.xml 清单中指定的组件,请发出 使用 Salesforce CLI 命令检索呼叫。在命令行中,运行此调用 替换为适当的文件路径:sf project retrieve start –manifest path/to/package.xml元数据是异步的, 基于文件的命令。您可以发出多个检索或部署请求,这些请求 当资源可用时,它们会自行运行。retrieve()使用此命令,您可以 发送请求以检索 包 .xml 清单。您的请求将排队等待,直到我们的系统准备就绪 处理您的检索调用。在您的请求被取消排队后,您的检索 调用已运行。客户端检查检索的状态并通知您 通话完成后。调用返回 选择的组件。当您使用 Salesforce CLI 发出检索调用时,所有 这些过程是自动化的。该命令允许源跟踪。源 跟踪包括有关您正在处理的修订版的信息,以及 当进行最后一次更改时,这使得源命令更多 对开发人员友好。要使用源跟踪,请确保在 组织。project retrieve start
  2. 或者,在终端中运行以下命令:sf project retrieve start –manifest path/to/package.xml –target-metadata-dir path/to/retrieve/dir此命令 以 MDAPI 格式(而不是源格式)检索组件,并且不会 允许源跟踪。在实践中,管理员更频繁地使用 mdapi 命令 因为这些命令不包括源跟踪。

为元数据 API 构建客户端应用程序

使用元数据 API 检索、部署、创建、更新或删除 组织的自定义项。最常见的用途是从 沙盒或测试组织添加到您的生产环境。元数据 API 适用于 管理自定义项和构建可以管理元数据模型的工具, 而不是数据本身。

Salesforce CLI 自动执行元数据 API 的基础调用。但是,您可以使用这些 直接使用您自己的客户端应用程序进行调用。本指南为您提供了所有信息 require 开始编写直接使用元数据 API 来管理自定义项的应用程序 为您的组织。它向您展示了如何开始使用基于文件的开发。为 基于 CRUD 的开发示例,请参阅 Java 基于 CRUD 的开发示例 同步调用。

先决条件

在开始使用元数据 API 之前,请确保完成这些先决条件。

  • 创建开发环境。我们强烈建议您使用沙盒,它是 生产组织。企业版、无限制版和性能版随附 免费的开发者沙盒。有关详细信息,请参阅 http://www.salesforce.com/platform/cloud-infrastructure/sandbox.jsp。或者,您可以使用 Developer Edition (DE) 组织。DE 组织提供对 Enterprise Edition 提供的所有功能,但受用户数量限制 以及存储空间的大小。DE 组织不是生产组织的副本/它提供了一个 您可以在其中构建和测试解决方案而不会影响 组织的数据。Developer Edition 帐户可在 https://developer.salesforce.com/signup 免费获得。
  • 标识具有“已启用 API”权限和“修改元数据”的用户 元数据 API 函数权限或修改所有数据权限。这些权限是 需要访问元数据 API 调用。注意如果用户需要访问元数据,但不需要 data,请启用“通过元数据 API 函数修改元数据”权限。否则 启用“修改所有数据”权限。
  • 安装 SOAP 客户机。元数据 API 适用于当前的 SOAP 开发环境, 包括但不限于 Visual Studio® .NET 和 Web 服务连接器 (WSC)。在本文档中,我们提供了基于 WSC 和 JDK 6(Java 平台)的 Java 示例 标准版开发套件 6)。若要运行示例,请先下载最新的 force-wsc JAR 文件及其依赖项来自 mvnrepository.com/artifact/com.force.api/force-wsc/。列出了依赖项 在选择版本时的页面上。注意开发平台在以下方面各不相同 SOAP 实现。某些开发平台的实现差异可以 阻止访问元数据 API 中的部分或全部功能。

步骤 1:生成或获取 Web 服务 组织的 WSDL

若要访问元数据 API 调用,需要 Web 服务描述语言 (WSDL) 文件。The WSDL file 定义可供您使用的 Web 服务。您的开发平台使用 此 WSDL 生成存根代码以访问它定义的 Web 服务。您可以获取 WSDL 文件(如果有) 访问 Salesforce 用户界面中的 WSDL 下载页面,您可以生成它 你自己。有关 WSDL 的更多信息,请参见 http://www.w3.org/TR/wsdl

在访问元数据 API 调用之前, 您必须通过身份验证才能使用调用来使用 Web 服务,该调用在企业中定义 WSDL 和合作伙伴 WSDL。因此,您还必须获得以下一项 这些 WSDL。login()具有“通过元数据 API 函数修改元数据”或“修改所有数据”权限的任何用户 可以下载 WSDL 文件以集成和扩展 Salesforce 平台。

注意

如果用户需要访问元数据,但不需要 data,请启用“通过元数据 API 函数修改元数据”权限。否则 启用“修改所有数据”权限。

步骤 3:演练 Java 示例代码中的示例代码使用企业 WSDL,但合作伙伴 WSDL 工作 同样好。

要为您的组织生成元数据和企业 WSDL 文件,请执行以下操作:

  1. 登录到您的 Salesforce 帐户。 您必须以管理员或具有“修改 所有数据“权限。
  2. 在“设置”中,输入“快速查找”框,然后选择“API”。API
  3. 单击“生成元数据 WSDL”,并将 XML WSDL 文件保存到您的文件中 系统。
  4. 单击“生成企业 WSDL”,并将 XML WSDL 文件保存到您的文件中 系统。

第 2 步:将 WSDL 文件导入到 开发平台

获得 WSDL 文件后,将它们导入到开发中 平台,以便您的开发环境可以生成必要的 用于生成客户端 Web 服务应用程序的对象。这 部分提供了 WSC 的示例说明。有关以下内容的说明 其他开发平台,请参阅平台的产品文档。

注意

导入 WSDL 文件的过程与元数据相同 和企业 WSDL 文件。

Java 环境说明 (WSC)

Java 环境通过 Java 对象访问 API,这些对象 充当服务器端对应物的代理。在使用 API 之前,您必须 首先从组织的 WSDL 文件生成这些对象。

每个 SOAP 客户端都有自己的工具用于此过程。对于 WSC,请使用 实用程序。wsdlc

注意

在运行 之前,必须在系统上安装并引用 WSC JAR 文件 在您的类路径中。您可以下载最新的 force-wsc JAR 文件 及其依赖项(依赖项在页面上列出时 从 mvnrepository.com/artifact/com.force.api/force-wsc/ 中选择一个版本。wsdlc的基本语法是:

wsdlc

java -classpath pathToWsc;pathToWscDependencies com.sforce.ws.tools.wsdlc pathToWsdl/WsdlFilename pathToOutputJar/OutputJarFilename

例如,在 Windows 上:

java –classpath force-wsc-30.0.0.jar;ST4-4.0.7.jar;antlr-runtime-3.5.jar com.sforce.ws.tools.wsdlc metadata.wsdl metadata.jar

在 Mac OS X 和 Unix 上,使用冒号而不是分号 类路径中的项:

java –classpath force-wsc-30.0.0.jar:ST4-4.0.7.jar:antlr-runtime-3.5.jar com.sforce.ws.tools.wsdlc metadata.wsdl metadata.jar

wsdlc生成 JAR 文件 以及用于创建客户端的 Java 源代码和字节码文件 应用。对企业 WSDL 重复此过程以创建 一个企业。JAR 文件。

步骤 3:演练 Java 示例代码

导入 WSDL 文件后,可以构建使用 元数据 API。此示例是编写自己的代码的良好起点。

在运行示例之前,请修改项目和代码,以便:

  1. 包括 WSC JAR、其依赖项以及您从 WSDL。注意尽管 WSC 具有其他依赖项,但仅以下示例 需要 Rhino (js-1.7R2.jar),您可以从 mvnrepository.com/artifact/rhino/js 下载。
  2. 使用您的用户名和密码更新方法中的 USERNAME 和 PASSWORD 变量。如果 您当前的 IP 地址不在组织的受信任 IP 范围内,您将 需要将安全令牌附加到密码中。MetadataLoginUtil.login()
  3. 如果您使用的是沙盒,请务必更改登录 URL。

登录实用程序

Java 用户可用于连接到 企业、合作伙伴和元数据 SOAP API。 创建对象并使用企业 WSDL 登录名登录 方法。然后,它检索并创建一个并连接到元数据 API 端点。 定义于 WSC。ConnectorConfigMetadataLoginUtilConnectorConfigsessionIdmetadataServerUrlConnectorConfigConnectorConfig

该类抽象化登录名 示例其他部分的代码,允许重用此代码的某些部分 无需更改不同的 Salesforce API。MetadataLoginUtil

import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.LoginResult;
import com.sforce.soap.metadata.MetadataConnection;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;

/**
 * Login utility.
 */
public class MetadataLoginUtil {

    public static MetadataConnection login() throws ConnectionException {
        final String USERNAME = "user@company.com";
        // This is only a sample. Hard coding passwords in source files is a bad practice.
        final String PASSWORD = "password"; 
        final String URL = "https://login.salesforce.com/services/Soap/c/59.0";
        final LoginResult loginResult = loginToSalesforce(USERNAME, PASSWORD, URL);
        return createMetadataConnection(loginResult);
    }

    private static MetadataConnection createMetadataConnection(
            final LoginResult loginResult) throws ConnectionException {
        final ConnectorConfig config = new ConnectorConfig();
        config.setServiceEndpoint(loginResult.getMetadataServerUrl());
        config.setSessionId(loginResult.getSessionId());
        return new MetadataConnection(config);
    }

    private static LoginResult loginToSalesforce(
            final String username,
            final String password,
            final String loginUrl) throws ConnectionException {
        final ConnectorConfig config = new ConnectorConfig();
        config.setAuthEndpoint(loginUrl);
        config.setServiceEndpoint(loginUrl);
        config.setManualLogin(true);
        return (new EnterpriseConnection(config)).login(username, password);
    }
}

注意

此示例使用用户和密码身份验证来获取会话 ID,该 ID 然后用于调用元数据 API。或者,您可以使用 OAuth 认证。使用 OAuth 向 Salesforce 发送请求后,将返回的 访问令牌,而不是会话 ID。例如,将访问令牌传递给 上的调用。了解如何使用 OAuth 在 Salesforce 中进行身份验证,请参阅使用以下方式对应用程序进行身份验证 Salesforce 帮助中的 OAuth。setSessionId()ConnectorConfig

基于文件的 Java 示例代码 发展

示例代码使用登录实用程序登录。然后它显示一个菜单 检索、部署和退出。

和调用都对名为 components.zip 的 .zip 文件进行操作。该调用将组织中的组件检索到 components.zip 中,并且该调用将 components.zip 中的组件部署到组织。如果保存示例 到您的计算机并执行它,首先运行 retrieve 选项,以便您有一个可以随后部署的组件.zip文件。后 检索调用,示例在循环中调用,直到操作完成。 同样,在部署调用之后,示例会在循环中进行检查,直到操作完成。retrieve()deploy()retrieve()deploy()checkRetrieveStatus()checkDeployStatus()

该调用使用清单文件执行以下操作: 确定要从组织中检索的组件。下面是一个示例包 .xml 清单文件。有关 清单文件结构,请参阅使用 Zip 文件部署和检索元数据。在此示例中,清单文件检索所有自定义对象。 自定义选项卡和页面布局。retrieve()

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>*</members>
        <name>CustomObject</name>
    </types>
    <types>
        <members>*</members>
        <name>CustomTab</name>
    </types>
    <types>
        <members>*</members>
        <name>Layout</name>
    </types>
    <version>59.0</version>
</Package>

请注意每个 API 调用后面的错误处理代码。

注意

此示例需要 API 版本 34.0 或 后。

import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.rmi.RemoteException;
import java.util.*;

import javax.xml.parsers.*;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import com.sforce.soap.metadata.*;

/**
 * Sample that logs in and shows a menu of retrieve and deploy metadata options.
 */
public class FileBasedDeployAndRetrieve {

    private MetadataConnection metadataConnection;

    private static final String ZIP_FILE = "components.zip";

    // manifest file that controls which components get retrieved
    private static final String MANIFEST_FILE = "package.xml";

    private static final double API_VERSION = 29.0;

    // one second in milliseconds
    private static final long ONE_SECOND = 1000;

    // maximum number of attempts to deploy the zip file
    private static final int MAX_NUM_POLL_REQUESTS = 50;

    private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    public static void main(String[] args) throws Exception {
        FileBasedDeployAndRetrieve sample = new FileBasedDeployAndRetrieve();
        sample.run();
    }

    public FileBasedDeployAndRetrieve() {
    }

    private void run() throws Exception {
        this.metadataConnection = MetadataLoginUtil.login();

        // Show the options to retrieve or deploy until user exits
        String choice = getUsersChoice();
        while (choice != null && !choice.equals("99")) {
            if (choice.equals("1")) {
                retrieveZip();
            } else if (choice.equals("2")) {
                deployZip();
            } else {
                break;
            }
            // show the options again
            choice = getUsersChoice();
        }
    }

    /*
     * Utility method to present options to retrieve or deploy.
     */
    private String getUsersChoice() throws IOException {
        System.out.println(" 1: Retrieve");
        System.out.println(" 2: Deploy");
        System.out.println("99: Exit");
        System.out.println();
        System.out.print("Enter 1 to retrieve, 2 to deploy, or 99 to exit: ");
        // wait for the user input.
        String choice = reader.readLine();
        return choice != null ? choice.trim() : "";
    }

    private void deployZip() throws Exception {
        byte zipBytes[] = readZipFile();
        DeployOptions deployOptions = new DeployOptions();
        deployOptions.setPerformRetrieve(false);
        deployOptions.setRollbackOnError(true);
        AsyncResult asyncResult = metadataConnection.deploy(zipBytes, deployOptions);
        DeployResult result = waitForDeployCompletion(asyncResult.getId());
        if (!result.isSuccess()) {
            printErrors(result, "Final list of failures:\n");
            throw new Exception("The files were not successfully deployed");
        }
        System.out.println("The file " + ZIP_FILE + " was successfully deployed\n");
    }

    /*
    * Read the zip file contents into a byte array.
    */
    private byte[] readZipFile() throws Exception {
        byte[] result = null;
        // We assume here that you have a deploy.zip file.
        // See the retrieve sample for how to retrieve a zip file.
        File zipFile = new File(ZIP_FILE);
        if (!zipFile.exists() || !zipFile.isFile()) {
            throw new Exception("Cannot find the zip file for deploy() on path:"
                + zipFile.getAbsolutePath());
        }

        FileInputStream fileInputStream = new FileInputStream(zipFile);
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int bytesRead = 0;
            while (-1 != (bytesRead = fileInputStream.read(buffer))) {
                bos.write(buffer, 0, bytesRead);
            }

            result = bos.toByteArray();
        } finally {
            fileInputStream.close();
        }
        return result;
    }

    /*
    * Print out any errors, if any, related to the deploy.
    * @param result - DeployResult
    */
    private void printErrors(DeployResult result, String messageHeader) {
        DeployDetails details = result.getDetails();
        StringBuilder stringBuilder = new StringBuilder();
        if (details != null) {
            DeployMessage[] componentFailures = details.getComponentFailures();
            for (DeployMessage failure : componentFailures) {
                String loc = "(" + failure.getLineNumber() + ", " + failure.getColumnNumber();
                if (loc.length() == 0 && !failure.getFileName().equals(failure.getFullName()))
                {
                    loc = "(" + failure.getFullName() + ")";
                }
                stringBuilder.append(failure.getFileName() + loc + ":" 
                    + failure.getProblem()).append('\n');
            }
            RunTestsResult rtr = details.getRunTestResult();
            if (rtr.getFailures() != null) {
                for (RunTestFailure failure : rtr.getFailures()) {
                    String n = (failure.getNamespace() == null ? "" :
                        (failure.getNamespace() + ".")) + failure.getName();
                    stringBuilder.append("Test failure, method: " + n + "." +
                            failure.getMethodName() + " -- " + failure.getMessage() + 
                            " stack " + failure.getStackTrace() + "\n\n");
                }
            }
            if (rtr.getCodeCoverageWarnings() != null) {
                for (CodeCoverageWarning ccw : rtr.getCodeCoverageWarnings()) {
                    stringBuilder.append("Code coverage issue");
                    if (ccw.getName() != null) {
                        String n = (ccw.getNamespace() == null ? "" :
                        (ccw.getNamespace() + ".")) + ccw.getName();
                        stringBuilder.append(", class: " + n);
                    }
                    stringBuilder.append(" -- " + ccw.getMessage() + "\n");
                }
            }
        }
        if (stringBuilder.length() > 0) {
            stringBuilder.insert(0, messageHeader);
            System.out.println(stringBuilder.toString());
        }
    }
    

    private void retrieveZip() throws Exception {
        RetrieveRequest retrieveRequest = new RetrieveRequest();
        // The version in package.xml overrides the version in RetrieveRequest
        retrieveRequest.setApiVersion(API_VERSION);
        setUnpackaged(retrieveRequest);

        AsyncResult asyncResult = metadataConnection.retrieve(retrieveRequest);
        RetrieveResult result = waitForRetrieveCompletion(asyncResult);

        if (result.getStatus() == RetrieveStatus.Failed) {
            throw new Exception(result.getErrorStatusCode() + " msg: " +
                    result.getErrorMessage());
        } else if (result.getStatus() == RetrieveStatus.Succeeded) {  
	        // Print out any warning messages
	        StringBuilder stringBuilder = new StringBuilder();
	        if (result.getMessages() != null) {
	            for (RetrieveMessage rm : result.getMessages()) {
	                stringBuilder.append(rm.getFileName() + " - " + rm.getProblem() + "\n");
	            }
	        }
	        if (stringBuilder.length() > 0) {
	            System.out.println("Retrieve warnings:\n" + stringBuilder);
	        }
	
	        System.out.println("Writing results to zip file");
	        File resultsFile = new File(ZIP_FILE);
	        FileOutputStream os = new FileOutputStream(resultsFile);
	
	        try {
	            os.write(result.getZipFile());
	        } finally {
	            os.close();
	        }
        }
    }

    private DeployResult waitForDeployCompletion(String asyncResultId) throws Exception {
        int poll = 0;
        long waitTimeMilliSecs = ONE_SECOND;
        DeployResult deployResult;
        boolean fetchDetails;
        do {
            Thread.sleep(waitTimeMilliSecs);
            // double the wait time for the next iteration

            waitTimeMilliSecs *= 2;
            if (poll++ > MAX_NUM_POLL_REQUESTS) {
                throw new Exception(
                    "Request timed out. If this is a large set of metadata components, " +
                    "ensure that MAX_NUM_POLL_REQUESTS is sufficient.");
            }
            // Fetch in-progress details once for every 3 polls
            fetchDetails = (poll % 3 == 0);

            deployResult = metadataConnection.checkDeployStatus(asyncResultId, fetchDetails);
            System.out.println("Status is: " + deployResult.getStatus());
            if (!deployResult.isDone() && fetchDetails) {
                printErrors(deployResult, "Failures for deployment in progress:\n");
            }
        }
        while (!deployResult.isDone());

        if (!deployResult.isSuccess() && deployResult.getErrorStatusCode() != null) {
            throw new Exception(deployResult.getErrorStatusCode() + " msg: " +
                    deployResult.getErrorMessage());
        }
        
        if (!fetchDetails) {
            // Get the final result with details if we didn't do it in the last attempt.
            deployResult = metadataConnection.checkDeployStatus(asyncResultId, true);
        }
        
        return deployResult;
    }

    private RetrieveResult waitForRetrieveCompletion(AsyncResult asyncResult) throws Exception {
    	// Wait for the retrieve to complete
        int poll = 0;
        long waitTimeMilliSecs = ONE_SECOND;
        String asyncResultId = asyncResult.getId();
        RetrieveResult result = null;
        do {
            Thread.sleep(waitTimeMilliSecs);
            // Double the wait time for the next iteration
            waitTimeMilliSecs *= 2;
            if (poll++ > MAX_NUM_POLL_REQUESTS) {
                throw new Exception("Request timed out.  If this is a large set " +
                "of metadata components, check that the time allowed " +
                "by MAX_NUM_POLL_REQUESTS is sufficient.");
            }
            result = metadataConnection.checkRetrieveStatus(
                    asyncResultId, true);
            System.out.println("Retrieve Status: " + result.getStatus());
        } while (!result.isDone());         

        return result;
    }

    private void setUnpackaged(RetrieveRequest request) throws Exception {
        // Edit the path, if necessary, if your package.xml file is located elsewhere
        File unpackedManifest = new File(MANIFEST_FILE);
        System.out.println("Manifest file: " + unpackedManifest.getAbsolutePath());

        if (!unpackedManifest.exists() || !unpackedManifest.isFile()) {
            throw new Exception("Should provide a valid retrieve manifest " +
                "for unpackaged content. Looking for " +
                unpackedManifest.getAbsolutePath());
        }

        // Note that we use the fully quualified class name because
        // of a collision with the java.lang.Package class
        com.sforce.soap.metadata.Package p = parsePackageManifest(unpackedManifest);
        request.setUnpackaged(p);
    }

    private com.sforce.soap.metadata.Package parsePackageManifest(File file)
            throws ParserConfigurationException, IOException, SAXException {
        com.sforce.soap.metadata.Package packageManifest = null;
        List<PackageTypeMembers> listPackageTypes = new ArrayList<PackageTypeMembers>();
        DocumentBuilder db =
                DocumentBuilderFactory.newInstance().newDocumentBuilder();
        InputStream inputStream = new FileInputStream(file);
        Element d = db.parse(inputStream).getDocumentElement();
        for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling()) {
            if (c instanceof Element) {
                Element ce = (Element) c;
                NodeList nodeList = ce.getElementsByTagName("name");
                if (nodeList.getLength() == 0) {
                    continue;
                }
                String name = nodeList.item(0).getTextContent();
                NodeList m = ce.getElementsByTagName("members");
                List<String> members = new ArrayList<String>();
                for (int i = 0; i < m.getLength(); i++) {
                    Node mm = m.item(i);
                    members.add(mm.getTextContent());
                }
                PackageTypeMembers packageTypes = new PackageTypeMembers();
                packageTypes.setName(name);
                packageTypes.setMembers(members.toArray(new String[members.size()]));
                listPackageTypes.add(packageTypes);
            }
        }
        packageManifest = new com.sforce.soap.metadata.Package();
        PackageTypeMembers[] packageTypesArray =
                new PackageTypeMembers[listPackageTypes.size()];
        packageManifest.setTypes(listPackageTypes.toArray(packageTypesArray));
        packageManifest.setVersion(API_VERSION + "");
        return packageManifest;
    }
}

元数据 API 开发人员指南

Salesforce 元数据

元数据是描述其他数据的数据。要了解 Salesforce 如何定义元数据, 将业务数据与 Salesforce 元数据进行对比。业务数据包括以下记录: 直接对应于您公司的业务,例如地址、帐户或产品。 Salesforce 元数据描述了架构、流程、表示、授权和常规 Salesforce 组织的配置。

要将 Salesforce 元数据与业务数据进行对比,请首先检查架构元数据的 描述业务数据的属性。例如,Salesforce 标准对象 Address 具有架构元数据和业务数据。 地址字段(如 、 和 )都是架构元数据。每个中的相应值 字段,例如邮寄地址、伊利诺伊州芝加哥和 60106,都是数据。 而个人 身份信息 (PII) 通常存在于业务数据中,元数据还可以包括 PII,例如自定义对象名称、报告名称等。Address TypeCityPostal Code

Salesforce 中的元数据还定义了组织的运作方式。例如,流程元数据 描述当用户按下“保存”按钮时发生的情况。演示文稿元数据问题 组织的布局和授权元数据决定了用户访问权限。Salesforce的 元数据还描述了组织的常规配置。例如,您可以配置 Chatter 阻止帖子中的表情符号。

元数据 API 适用于元数据类型和组件。元数据类型定义 应用程序元数据的结构。元数据组件是元数据类型的实例。 元数据类型的字段和值都是元数据。例如,元数据类型 CustomTab 表示显示内容的自定义选项卡。这 CustomTab 字段指示选项卡是否位于侧边栏面板上, 这是元数据确定表示的一个示例。元数据类型,如 CustomTab 构建元数据模型,描述组织的结构、显示或功能。 使用元数据 API 开发自定义项并构建用于管理元数据模型的工具, 而不是数据本身。hasSidebar

元数据 API 功能

元数据 API 的主要目的是在 Salesforce 组织在开发过程中。使用元数据 API 进行部署、检索、 创建、更新或删除自定义信息,例如自定义对象定义和 页面布局。元数据 API 不直接处理业务数据。要创建, 检索、更新或删除记录,例如客户或潜在客户,使用 SOAP API 或 REST API。

您可以使用以下两种方式之一移动元数据。第一种方法是使用元数据 API 和调用。管理员通常使用 and 调用来移动完整的元数据模型。这些调用 最适合开发的最后阶段,例如将经过测试的自定义项部署到 生产组织。deploy()retrieve()deploy()retrieve()

第二种方法是源推送和拉取命令,这些命令仅移动元数据中的更改。 这些命令使用源跟踪,这使得它们对开发人员更友好,也更好 用于中间开发阶段。

元数据 API 的用例

在开发过程中,使用元数据 API 在组织之间移动元数据 周期。元数据 API 还用于从 发展。

要了解如何使用元数据 API,假设您是 Zephyrus 的 Salesforce 开发人员 搬迁服务。Zephyrus 是一家人才流动公司,帮助公司开发流程 用于国内和国际员工搬迁。Zephyrus 正在向亚洲和南部扩张 美国,并希望为这两个地区增加定向服务。迎新服务包括 在住房和学校搜索、地区旅游和交通方面提供国内援助 信息。

您的开发团队必须将这些新的定向服务添加到其现有组织中。产品 例如,国内方向是可以在 Salesforce 中自定义的对象。当您添加 对象并自定义您的组织,您可以更改其元数据。创建 自定义产品是元数据 API 可以提供帮助的地方。

在开发过程中使用元数据 API

目前,Zephyrus 拥有为其他 国家。若要开始构建新产品自定义项,需要现有的 Zephyrus 生产 Salesforce 组织中的配置位于单独的存储库中。这 生产组织的配置都是元数据。要将生产元数据保存在 存储库,将元数据从 Zephyrus 生产组织移动到本地文件系统。

将元数据从生产环境移动到本地文件系统

在不进行开发更改的情况下进行开发更改 影响现有配置,请使用元数据 API 将元数据移动到本地文件 系统。接下来,将元数据从本地文件系统推送到可共享的存储库,以便 发展。

检索到所有 Zephyrus 元数据后,您可以在本地或临时组织中进行开发。抓 组织是没有数据的一次性 Salesforce 环境。许多开发人员同时使用这两种工具 一起。在本地加载文件和进行更改比在临时执行要快得多 组织。开发人员通常在其本地文件系统上构建自定义项,并在临时运行测试 组织。在测试和开发时,在本地文件系统和临时组织之间移动更改。

将元数据更改移入和移出临时组织

您可以使用临时组织 使用本地文件系统来开发和测试对元数据的更改。要移动更改,请执行以下操作 在本地与 scratch 组织进行本地创建,使用元数据 API。

Zephyrus 开发团队的其他成员都有自己的自定义项。在开发和 您可以自行测试,现在是团队集成更改并在沙盒中运行测试的时候了。 沙盒是用于开发和测试集成的开发环境。

将元数据移动到集成点的沙盒

在开发过程中,使用元数据 用于将元数据移动到沙盒的 API,用于集成更改、测试和协作 团队。

在团队构建定向服务自定义并完成测试后,部署 这些组件使用元数据 API 进行生产。

将元数据部署到生产环境

在最后一步 开发周期,将自定义项从源代码管理系统(如 Git)移动到生产环境 使用元数据 API。

其他用例

您可以使用元数据 API 在 Salesforce 中进行较大的更改,例如拆分和合并 生产组织。

例如,Zephyrus 希望将公司拆分为两个部门,一个部门专门从事 国内搬迁和另一个国际搬迁。在这种情况下,您拆分了 Zephyrus 的 Salesforce 组织并决定哪些元数据属于哪个组织。元数据 API 可以将元数据移动到 新组织。

然后,假设 Zephyrus 收购了 Apollo Global Relocation,两家公司都使用 Salesforce。 要整合信息,您可以使用元数据 API 将 Apollo 组织合并到 Zephyrus 中 组织。

移动生产级更改的元数据

使用元数据 API 移动元数据 在大型更改期间,例如合并或拆分 Salesforce 组织。

您可以使用元数据 API 在开发过程中进行配置更改,这些更改是 对于其他 API 调用来说太大。例如,Zephyrus 支持多种语言,因为它们 全球客户。要为对象翻译不同的语言,请包含一个对象 每种语言的翻译文件。

进行大型元数据配置更改

元数据更适合 用于向组织部署大型更改的其他 API。

将元数据从生产环境移动到本地文件系统

在不影响现有 配置,请使用元数据 API 将元数据移动到本地文件系统。接下来,推送 元数据从本地文件系统传输到可共享的存储库,用于 发展。

在 Salesforce 上构建自定义项时,必须保留 开发周期中的现有组织。若要在不影响自定义项的情况下构建自定义项,请执行以下操作 生产组织,将生产元数据保存在版本控制系统中。Git 集成最好 使用 SFDX 工具。

首先,将所需的元数据从生产组织移动到本地文件系统。移动 元数据到本地计算机,请使用检索调用而不是源拉取。接下来,推动你的 文件复制到团队成员可通过 Git 命令访问的存储库中。这 存储库现在是团队开发的生产元数据的原始来源 周期。

现在,您的生产元数据已存储在存储库中,请将必要的元数据移回 到本地文件系统以开始开发工作。

将元数据更改移入和移出临时组织

使用临时组织来开发和测试对元数据的更改。您可以 使用 Salesforce CLI 或 Salesforce 在临时组织内部或外部执行开发 VS Code 的扩展,利用元数据 API 的强大功能。

临时组织是空的,以便开发人员可以指定确切的元数据和数据 从源代码管理系统中包含。临时组织的生命周期在 创建,1-30 天。它们是短暂的,以确保真相的来源始终是来源 控制系统,而不是组织本身。

您可以使用 Salesforce 将元数据从源代码管理系统或临时组织移动到临时组织 由于临时组织使用源跟踪来识别更改,因此 CLI 是最 在本地存储库和临时组织之间移动元数据的有效方法。继续 遍历在本地文件系统和 暂存组织,直到开发完成。

将元数据移动到集成点的沙盒

在开发过程中,使用元数据 API 将元数据移动到沙盒 集成更改、测试并与您的团队协作。

在临时组织或本地文件系统中自行开发后,将 团队在沙盒中的集成点。沙盒是您可以使用的开发环境 集成和测试来自多个开发人员的更改。管理员通常会创建和分配沙盒。 要在 Salesforce UI 上创建沙盒,请导航到设置。接下来,在“快速查找”框中,搜索 用于沙盒。

您有多个级别的沙盒可供选择,其中包含不同的数据量。这 Developer Sandbox 和 Developer Pro Sandbox 是用于构建的开发环境 对虚构数据进行自定义和测试更改。“部分复制”沙箱和“完整”沙箱是 加载了生产数据副本的测试环境。将元数据移动到不同的沙盒 使用元数据 API 部署命令,具体取决于您的开发和测试需求。

在元数据 API 之外,管理员通常使用更改集从一个 沙盒到另一个。与元数据 API 调用不同,您必须手动构建更改集。要添加 组件到持续集成系统中更容易,您可以自动执行元数据 API 调用 Salesforce 命令行界面。

将元数据部署到生产环境

在开发周期的最后一步中,将自定义项从 源代码控制系统(如 Git)使用元数据 API 投入生产。

当您的团队完成集成测试并准备好部署到生产环境时,请将 完成从本地环境到存储库的自定义。对于版本,请移动 通过将更新的存储库拉回本地,将元数据从存储库拉回生产环境 环境。接下来,使用元数据 API deploy 将元数据部署到生产环境 叫。

将元数据移动到生产环境需要部署调用而不是推送命令,因为 deploy 调用部署整个元数据模型,而不仅仅是元数据中的更改。

部署最近的验证

常规部署调用会执行可能需要很长时间才能完成的自动化 Apex 测试。自 跳过已验证组件的测试并快速将组件部署到生产环境,使用 Deploy 最近的验证选项。

移动生产级更改的元数据

使用元数据 API 在大型更改期间移动元数据,例如 合并或拆分 Salesforce 组织。

要拆分组织,请先检索要移动的元数据。然后,使用 deploy 调用 将这些配置推送到新组织。同样,要合并两个组织,请检索 来自一个组织的现有元数据。接下来,使用 deploy 调用将元数据从 一个组织到另一个组织。

进行大型元数据配置更改

元数据 API 比其他 API 更适合部署大型 对 Salesforce 组织的更改。

元数据 API 和调用是基于文件的,因此 异步。使用同步命令时,大型配置更改需要 加载时间过长。相反,部署和检索调用会以异步方式开始 完成时通知您的流程。由于基于文件的调用是异步的, 元数据 API 还可以处理部署请求队列。deploy()retrieve()

元数据 API 发行说明

使用 Salesforce 发行说明了解 元数据 API。

有关影响 Salesforce Platform 的更新和更改,包括元数据 API,请参阅 API 发行说明。

有关新的、已更改的和已弃用的元数据类型以及特定于元数据 API 的其他更改,请参阅 Salesforce 发行说明中的元数据 API。

元数据 API 开发人员工具

在 Salesforce CLI 上使用适用于 Visual Studio Code 的 Salesforce 扩展访问元数据 API 命令。Salesforce CLI 和 Salesforce Extensions for Visual Studio Code 简化了 使用元数据 API 的过程。访问元数据中功能的最简单方法 API 是使用 Salesforce Extensions for Visual Studio Code 或 Salesforce 这两个工具都建立在元数据 API 之上,并使用标准工具来 简化元数据 API 的使用。

  • 适用于 Visual Studio Code 的 Salesforce 扩展包括以下工具: 在 Salesforce 平台上使用轻量级、可扩展的 VS Code 进行开发 编辑 器。这些工具提供了与开发组织合作的功能 (临时组织、沙盒和 DE 组织)、Apex、Aura 组件和 视觉力。
  • 如果您使用脚本或命令行,Salesforce CLI 是理想的选择 在本地目录和 Salesforce 之间移动元数据 组织。

有关 Salesforce Extensions for Visual 的更多信息 Studio Code 或 Salesforce CLI,请参阅 Salesforce 工具和工具包。

如果您更喜欢构建自己的客户端应用程序,则 Metadata API 的基础调用 已公开供您直接使用。本指南为您提供有关工作的更多信息 直接使用元数据 API。

您可以使用元数据 API 来管理设置和自定义信息(元数据) Salesforce的。例如:

  • 将自定义项导出为 XML 元数据文件。请参阅使用 Zip 文件和 retrieve()。
  • 在组织之间迁移配置更改。参见 deploy() 和 retrieve()。
  • 使用 XML 元数据文件修改现有自定义项。参见 deploy() 和 retrieve()。
  • 以编程方式管理自定义项。请参阅基于 CRUD 的元数据开发。

您可以在 Developer Edition 或沙箱中修改测试组织中的元数据,然后进行部署 测试了对 Enterprise、Unlimited 或 Performance Edition 中生产组织的更改。您可以 此外,还可以创建脚本,以使用自定义对象、自定义字段和其他对象填充新组织 组件。

支持的 Salesforce 版本

若要使用元数据 API,您的组织必须使用 Enterprise Edition, Unlimited Edition、Performance Edition 或 Developer Edition。如果您是现有 想要升级到 Enterprise、Unlimited 或 Performance Edition 的 Salesforce 客户, 请联系您的客户代表。

我们强烈建议您使用沙盒,它是 生产组织。企业版、无限制版和性能版随附 免费的开发者沙盒。有关详细信息,请参阅 http://www.salesforce.com/platform/cloud-infrastructure/sandbox.jsp。

或者,您可以使用 Developer Edition (DE) 组织。DE 组织提供对 Enterprise Edition 提供的所有功能,但受用户数量限制 以及存储空间的大小。DE 组织不是生产组织的副本/它提供了一个 您可以在其中构建和测试解决方案而不会影响 组织的数据。Developer Edition 帐户可在 https://developer.salesforce.com/signup 免费获得。

注意

元数据组件必须在组织中可见,元数据 API 才能对其执行操作。此外,一个 用户必须具有 API Enabled 权限才能访问元数据组件。

专业版元数据 API 访问

ISV 合作伙伴可以请求对 Professional Edition 组织的元数据 API 访问权限,这些应用程序具有以下特点 已通过 AppExchange 安全审核。访问权限是通过 API 令牌(客户端 ID)授予的。 此特殊密钥使应用程序能够向客户的 Professional 进行元数据 API 调用 版本组织。

作为 ISV 合作伙伴,可以按照以下步骤请求元数据 API 访问权限。

  1. 提交您的应用进行安全审核。请参阅安全性中的步骤 在 ISVForce 中查看 指南。
  2. 在您的应用程序获得批准后,在合作伙伴社区中的 AppExchange 和功能请求 |API 令牌请求,并为令牌类型指定 SOAP。

若要调用元数据 API,请在调用中将 API 令牌追加到 CallOptions SOAP 标头。

元数据 API 编辑访问权限

要使用元数据 API,用户必须具备以下条件。

  • 以下版本之一:企业版、无限制版或开发人员版
  • “通过元数据 API 函数修改元数据”或“修改所有数据”权限
  • 允许使用他们想要的元数据所支持的功能的权限 修改
  • 启用其部署工具(例如 Salesforce CLI、更改集或 蚂蚁迁移工具

通过元数据 API 函数修改元数据权限,用户可以访问和编辑 元数据通过元数据 API,只要用户具有访问所需的任何其他权限 某些元数据类型。此附加权限信息列在元数据中 每种元数据类型的 API 开发人员指南。具有“修改所有数据”权限的用户 可以访问和编辑所有数据。

“通过元数据 API 函数修改元数据”权限不影响直接 使用设置 UI 页面自定义元数据,因为这些页面不使用元数据 API 更新。

某些元数据(如 Apex)在系统上下文中执行,因此请注意如何委派 通过元数据 API 函数权限修改元数据。通过元数据修改元数据 API 函数权限允许部署 Apex 元数据,但不允许某些 Apex 仍需要“修改所有数据”权限的开发和调试功能。

在以下情况下,将自动启用“通过元数据 API 函数修改元数据”权限 选择“部署更改集”或“创作 Apex”权限。

当“管理提示”用户权限和“通过元数据修改元数据”API 函数时 权限组合在一起,用户可以在 Lightning Experience 中管理应用程序内指导。

开发平台

元数据 API 支持基于文件和基于 CRUD 的开发。

基于文件的开发

声明性或基于文件的异步元数据 API deploy() 和 retrieve() 操作 部署或检索包含组件的文件 一组文件夹和一个名为 package.xml 的清单文件。查看更多 信息,请参阅部署和检索 元数据。访问基于文件的功能的最简单方法是使用 适用于 Visual Studio Code 的 Salesforce 扩展或 Ant 迁移工具。.zip

基于 CRUD 的开发

CRUD 元数据 API 调用作用于元数据组件 其方式类似于企业 WSDL 中同步 API 调用的行为方式 在对象上。有关企业 WSDL 的更多信息,请参见《SOAP API 开发人员指南》。

标准符合性

元数据 API 的实现符合以下规范:

标准名称网站
简单对象访问协议 (SOAP) 1.1http://www.w3.org/TR/2000/NOTE-SOAP-20000508/
Web 服务描述语言 (WSDL) 1.1http://www.w3.org/TR/2001/NOTE-wsdl-20010315
WS-I 基本概要文件 1.1http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html

元数据 API 支持策略

Salesforce 支持以前版本的元数据 API。但是,您的新客户端应用程序 应使用最新版本的 Lightning 平台元数据 API WSDL 文件,以完全 利用更丰富的功能和更高的效率。

向后兼容性

Salesforce 努力使使用 Lightning 平台时的向后兼容性变得容易。

每个新的 Salesforce 版本都由两个组件组成:

  • 驻留在 Salesforce 系统上的平台软件的新版本
  • API 的新版本

例如,Spring ’07 版本包括 API 版本 9.0 和 Summer ’07 版本 包括 API 版本 10.0。

我们维护对平台软件各个版本的每个 API 版本的支持。该 API 是 向后兼容,因为为使用给定 API 版本而创建的应用程序将 在将来的平台软件版本中继续使用相同的 API 版本。

Salesforce 不保证针对一个 API 版本编写的应用程序能够正常工作 对于未来的 API 版本:方法签名和数据表示形式的更改通常是 当我们继续增强 API 时,需要。但是,我们努力使 API 保持一致,以免 版本到版本,将应用程序移植到较新的 API 所需的更改(如果有)最少 版本。

例如,使用 Spring 附带的 API 版本 9.0 编写的应用程序 ’07 版本,将继续在 Summer ’07 版本上使用 API 版本 9.0, 以及以后的版本。但是,同一应用程序可能无法与 API 一起使用 版本 10.0,无需对应用程序进行修改。

API 生命周期终止政策

查看支持、不支持的元数据 REST 和 SOAP API 版本,或者 不能利用的。

Salesforce 承诺支持每个 API 版本至少 3 个 自首次发布之日起的年。为了提高质量和性能 API,有时不再支持超过 3 年的版本。

Salesforce 通知使用计划的 API 版本的客户 为 折旧 至少 1 年后,对版本的支持将结束。

Salesforce API 版本版本支持状态版本停用信息
版本 31.0 至 59.0支持。
版本 21.0 至 30.0截至 22 年夏季,这些版本已被弃用,并且没有 Salesforce 支持的时间更长。从 25 年夏季开始,这些 版本将停用且不可用。Salesforce Platform API 版本 21.0 到 30.0 停用
版本 7.0 至 20.0自 22 年夏季起,这些版本已停用,并且 不能利用的。Salesforce Platform API 版本 7.0 到 20.0 停用

如果你 请求任何资源或使用已停用的 API 版本 REST API 中的操作 返回错误代码。410:GONE

如果您要求任何 资源或使用已停用的 API 版本中的操作,SOAP API 将返回错误代码。500:UNSUPPORTED_API_VERSION

识别 从旧的或不受支持的 API 版本发出的请求,请使用 API 总使用量事件类型。

相关资源

Salesforce 开发人员网站提供一整套开发人员工具包、示例代码、示例 SOAP 消息、基于社区的支持和其他资源可帮助您进行开发 项目。请务必访问 https://developer.salesforce.com/page/Getting_Started 了解更多信息 信息,或访问 https://developer.salesforce.com/signup 注册一个免费的 Developer Edition 帐户。

您可以访问以下网站以了解有关 Salesforce 应用程序的更多信息:

  • Salesforce 开发人员提供有用的信息 开发 人员。
  • Salesforce 用于 有关 Salesforce 应用程序的信息。
  • 闪电平台 AppExchange,用于访问为 Salesforce 创建的应用程序。
  • 开拓者 社区提供服务,以确保 Salesforce 客户取得成功。