Salesforce API(9)Web服务

学习目标

完成本单元后,您将能够:

  • 描述两种类型的Apex Web服务并提供这些服务的高级概述。
  • 创建一个包含每个HTTP方法的方法的Apex REST类。
  • 使用端点调用自定义的Apex REST方法。
  • 通过以JSON格式发送请求主体,将数据传递给自定义的Apex REST方法。
  • 编写Apex REST方法的测试方法,并在测试REST请求中设置属性。
  • 通过调用具有参数值的方法编写Apex REST方法的测试方法。

将Apex类公开为Web服务

您可以将Apex类方法公开为REST或SOAP Web服务操作。通过使您的方法可以通过Web进行调用,您的外部应用程序可以与Salesforce集成以执行各种漂亮的操作。
例如,假设贵公司的呼叫中心正在使用内部应用程序来管理本地资源。预计客户支持代表将使用相同的应用程序来执行其日常工作,包括管理Salesforce中的案例记录。通过使用一个接口,代表可以查看和更新​​案例记录并访问内部资源。该应用程序调用Apex Web服务类来管理Salesforce案例记录。

公开一个类作为REST服务

使您的Apex类可用作REST Web服务非常简单。定义您的类为全局,并将方法定义为全局静态。为类和方法添加注释。例如,此示例Apex REST类使用一种方法。 getRecord方法是一个自定义的REST API调用。它使用@HttpGet进行注释,并被GET请求调用。

@RestResource(urlMapping='/Account/*')
global with sharing class MyRestResource {
    @HttpGet
    global static Account getRecord() {
        // 添加你的代码
    }
}
正如你所看到的,这个类用@RestResource注解(urlMapping =’/ Account / *)。 Apex REST的基本端点是https://yourInstance.salesforce.com/services/apexrest/。 URL映射附加到基本端点以形成REST服务的端点。例如,在类示例中,REST端点是https://yourInstance.salesforce.com/services/apexrest/Account/。对于您的组织,它可能看起来像https://yourInstance.salesforce.com/services/apexrest/Account/。

URL映射区分大小写,可以包含通配符(*)。

将每个暴露的方法定义为全局静态,并添加注释以将其与HTTP方法相关联。以下注释可用。每个Apex类中只能使用一次每个注释。

注解 行动 细节
@HttpGet Read 读取或检索记录。
@HttpPost Create 创建记录。
@HttpDelete Delete 删除记录。
@HttpPut Upsert 通常用于更新现有记录或创建记录。
@HttpPatch Update 通常用于更新现有记录中的字段。

公开一个类作为SOAP服务

使您的Apex类作为SOAP Web服务可用,就像使用REST一样简单。将您的课程定义为全球课程。将webservice关键字和静态定义修饰符添加到您要公开的每个方法。 webservice关键字提供对其添加方法的全局访问权限。

例如,这里有一个方法的示例类。 getRecord方法是一个定制的SOAP API调用,它返回一个Account记录。

global with sharing class MySOAPWebService {
    webservice static Account getRecord(String id) {
        // 添加你的代码
    }
}

外部应用程序可以通过使用类WSDL文件来调用您的自定义Apex方法作为Web服务操作。从类详细信息页面为您的类生成此WSDL,可从“安装程序”中的“Apex类”页面进行访问。通常,您可以将WSDL文件发送给第三方开发人员(或者自己使用)来编写Web服务的集成。

由于平台安全性是一流的Salesforce公民,因此您的Web服务需要身份验证。除了Apex类WSDL外,外部应用程序还必须使用Enterprise WSDL或Partner WSDL作为登录功能。

Apex REST演练

现在有趣的东西。接下来的几个步骤将引导您完成构建Apex REST服务的过程。首先,创建作为REST服务公开的Apex类。然后你尝试从客户端调用几个方法,最后编写单元测试。有相当多的代码,但这将是值得的努力!
您的Apex课程管理案例记录。该类包含五个方法,每个方法对应一个HTTP方法。例如,当客户端应用程序调用GET HTTP方法的REST调用时,将调用getCaseById方法。

由于该类是使用/ Cases / *的URL映射定义的,因此用于调用此REST服务的端点是以https://yourInstance.salesforce.com/services/apexrest/Cases/开头的任何URI。

我们建议您也考虑对API端点进行版本控制,以便在不破坏现有代码的情况下提供功能升级。您可以创建两个指定 /Cases/v1/*/Cases/v2/* 的URL映射的类来实现此功能。

让我们开始创建一个Apex REST类。

  1. 从设置档(打开设备齿轮图标Setup gear icon)打开开发者控制台。
  2. 在开发者控制台中,选择 File | New | Apex Class.
  3. 对于课程名称,输入CaseManager,然后单击 OK.
  4. 将自动生成的代码替换为以下类定义。
    @RestResource(urlMapping='/Cases/*')
    global with sharing class CaseManager {
    
        @HttpGet
        global static Case getCaseById() {
            RestRequest request = RestContext.request;
            // 从URL的末尾抓取caseId
            String caseId = request.requestURI.substring(
              request.requestURI.lastIndexOf('/')+1);
            Case result =  [SELECT CaseNumber,Subject,Status,Origin,Priority
                            FROM Case
                            WHERE Id = :caseId];
            return result;
        }
    
        @HttpPost
        global static ID createCase(String subject, String status,
            String origin, String priority) {
            Case thisCase = new Case(
                Subject=subject,
                Status=status,
                Origin=origin,
                Priority=priority);
            insert thisCase;
            return thisCase.Id;
        }   
    
        @HttpDelete
        global static void deleteCase() {
            RestRequest request = RestContext.request;
            String caseId = request.requestURI.substring(
                request.requestURI.lastIndexOf('/')+1);
            Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
            delete thisCase;
        }     
    
        @HttpPut
        global static ID upsertCase(String subject, String status,
            String origin, String priority, String id) {
            Case thisCase = new Case(
                    Id=id,
                    Subject=subject,
                    Status=status,
                    Origin=origin,
                    Priority=priority);
            // 通过Case Id匹配,如果存在的话。
            // 否则,创建新的案例。
            upsert thisCase;
            // 返回案例ID。
            return thisCase.Id;
        }
    
        @HttpPatch
        global static ID updateCaseFields() {
            RestRequest request = RestContext.request;
            String caseId = request.requestURI.substring(
                request.requestURI.lastIndexOf('/')+1);
            Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
            // 将JSON字符串反序列化为名称 - 值对
            Map<String, Object> params = (Map<String, Object>)JSON.deserializeUntyped(request.requestbody.tostring());
            // 遍历每个参数字段和值
            for(String fieldName : params.keySet()) {
                // 设置Case sObject的字段和值
                thisCase.put(fieldName, params.get(fieldName));
            }
            update thisCase;
            return thisCase.Id;
        }    
    
    }
  5. 按下CTRL + S保存。

使用POST方法创建一个记录

让我们使用您刚刚创建的Apex REST类,并获得一些乐趣。首先,我们将调用POST方法来创建一个案例记录。
要调用您的REST服务,您需要使用… REST客户端!几乎可以使用任何REST客户端,例如您自己的API客户端,cURL命令行工具或PHP的curl库。我们将使用Workbench工具作为我们的REST客户端应用程序,但稍后我们将会看看cURL。

Apex REST支持两种格式来表示资源:JSON和XML。 JSON表示在请求或响应的主体中默认传递,格式由HTTP头中的Content-Type属性指示。由于JSON比XML更易于阅读和理解,因此该设备仅使用JSON。在这一步中,您将以JSON格式发送案例记录。

Apex REST支持OAuth 2.0和会话认证机制。简而言之,这意味着我们使用行业标准来保证您的应用程序和数据安全。幸运的是,您可以使用Workbench来简化测试。 Workbench是一个功能强大的基于Web的工具套件,供管理员和开发人员通过Force.com API与组织进行交互。使用Workbench,您在使用您的用户名和密码登录到Salesforce时使用会话身份验证。而您使用REST资源管理器来调用您的REST服务。

  1. 导航到 https://workbench.developerforce.com/login.php.
  2. 对于环境,请选择 Production.
  3. 从“API版本”下拉列表中选择最新的API版本。
  4. 接受服务条款,然后单击 Login with Salesforce.
  5. 要允许工作台访问您的信息,请单击 Allow.
  6. 输入您的登录凭据,然后单击 Log in to Salesforce.
  7. 登录后,选择 utilities | REST Explorer.
  8. 选择 POST.
  9. REST Explorer接受的URL路径相对于您的组织的实例URL。仅提供追加到实例URL的路径。在相对URI输入字段中,将缺省URI替换为/services/apexrest/Cases/.
  10. 对于请求主体,插入要插入的对象的以下JSON字符串表示形式。
    {
      "subject" : "Bigfoot Sighting!",
      "status" : "New",
      "origin" : "Phone",
      "priority" : "Low"
    }
  11. 点击 Execute.
    该调用调用与POST HTTP方法关联的方法,即createCase方法。
  12. 要查看返回的响应,请单击 Show Raw Response.
    返回的响应与此响应类似。该响应包含新案例记录的ID。您的ID值可能与50061000000t7kYAAQ不同。保存您的ID值以便在下一步中使用。

    HTTP/1.1 200 OK
    Date: Wed, 07 Oct 2015 14:18:20 GMT
    Set-Cookie: BrowserId=F1wxIhHPQHCXp6wrvqToXA;Path=/;Domain=.salesforce.com;Expires=Sun, 06-Dec-2015 14:18:20 GMT
    Expires: Thu, 01 Jan 1970 00:00:00 GMT
    Content-Type: application/json;charset=UTF-8
    Content-Encoding: gzip
    Transfer-Encoding: chunked
    
    "50061000000t7kYAAQ" 

使用自定义GET方法检索数据

按照以前类似的步骤,使用Workbench来调用GET HTTP方法。

  1. 在Workbench中,选择 GET.
  2. 输入URI /services/apexrest/Cases/<Record ID>,将 <Record ID> 替换为您在上一步中创建的记录的ID。
  3. 点击 Execute.

    该调用调用与GET HTTP方法关联的方法,即getCaseById方法。

  4. 要查看返回的响应,请单击 Show Raw Response.

    返回的响应与此响应类似。响应包含为新的案例记录查询方法的字段。

    HTTP/1.1 200 OK
    Date: Wed, 07 Oct 2015 14:28:20 GMT
    Set-Cookie: BrowserId=j5qAnPDdRxSu8eHGqaRVLQ;Path=/;Domain=.salesforce.com;Expires=Sun, 06-Dec-2015 14:28:20 GMT
    Expires: Thu, 01 Jan 1970 00:00:00 GMT
    Content-Type: application/json;charset=UTF-8
    Content-Encoding: gzip
    Transfer-Encoding: chunked
    
    {
      "attributes" : {
        "type" : "Case",
        "url" : "/services/data/v34.0/sobjects/Case/50061000000t7kYAAQ"
      },
      "CaseNumber" : "00001026",
      "Subject" : "Bigfoot Sighting!",
      "Status" : "New",
      "Origin" : "Phone",
      "Priority" : "Low",
      "Id" : "50061000000t7kYAAQ"
    }
    

使用cURL检索数据

每个好的开发者至少应该知道三件事:1)如何制作一个自己动画的GIF,吃自己喜欢的冰淇淋; 2)pi的值到小数点后25位; 3)如何使用cURL。前两个超出了本模块的范围,所以我们将专注于最后一个。

cURL是一个使用URL语法获取或发送文件的命令行工具。使用REST端点时,它非常方便。您可以使用cURL来调用GET HTTP方法,而不是使用Workbench作为Apex REST服务。每当您“ROCK”您的REST端点时,您传递会话ID进行授权。在Workbench中工作时,你被宠坏了,因为在你登录之后,它会在你的封面下传递会话ID。

要获取会话ID,您首先在Salesforce组织中创建一个连接的应用程序并启用OAuth。在这种情况下,您的客户端应用程序cURL使用连接的应用程序连接到Salesforce。按照这些说明创建一个连接的应用程序,为您提供消费者密钥和消费者密钥,您需要获得您的会话ID。为连接的应用程序选择OAuth范围时,请选择“访问和管理数据(api)”范围。连接的应用程序可能需要5到10分钟才能完成设置。准备就绪后,对凭证和连接的应用程序使用以下cURL命令。

curl -v https://login.salesforce.com/services/oauth2/token -d "grant_type=password" -d "client_id=<your_consumer_key>" -d "client_secret=<your_consumer_secret>" -d "username=<your_username>" -d "password=<your_password_and_security_token>" -H 'X-PrettyPrint:1'
如果全部成功,则结果包含access_token,这是您的组织的会话ID和instance_url。

cURL response with access token

现在输入您的cURL命令(将与以下内容类似),以调用您的Apex REST服务并返回案例信息。

curl https://yourInstance.salesforce.com/services/apexrest/Cases/<Record_ID> -H 'Authorization: Bearer <your_session_id>' -H 'X-PrettyPrint:1'
按Enter后,您会看到类似于以下内容的内容。现在你已经是一个命令行的主人,随心所欲地为你的内容添加cURL,jq,sed,awk和grep。有关cURL的更多信息,请参阅参考资料部分。

cURL and response from the command line

使用自定义PUT或PATCH方法更新数据

您可以使用PUT或PATCH HTTP方法更新记录。 PUT方法要么更新整个资源(如果存在的话),要么创建资源(如果它不存在)。 PUT本质上是一个upsert方法。 PATCH方法只更新现有资源的指定部分。在Apex中,更新操作只更新指定的字段,不覆盖整个记录。我们将编写一些Apex代码来确定我们的方法是更新还是上传。

使用PUT方法更新数据

您添加到CaseManager类的upsertCase方法实现了PUT操作。这里包括这个方法供您参考。该方法使用内置的upsert Apex DML方法通过匹配ID值来创建或覆盖大小写记录字段。如果在请求的主体中发送了一个ID,则会使用它填充案例sObject。否则,创建没有ID的案例sObject。 upsert方法用填充的case sObject调用,DML语句做其余部分。瞧!

@HttpPut
global static ID upsertCase(String subject, String status,
    String origin, String priority, String id) {
    Case thisCase = new Case(
        Id=id,
        Subject=subject,
        Status=status,
        Origin=origin,
        Priority=priority);
    // 通过Id匹配大小写,如果存在的话。
    // 否则,创建新的案例。
    upsert thisCase;
    // 返回案例ID。
    return thisCase.Id;
}
要调用PUT方法:

  1. 在Workbench REST Explorer中,选择 PUT.
  2. 对于URI,输入 /services/apexrest/Cases/.
  3. upsertCase方法期望字段值在请求主体中传递。为请求主体添加以下内容,然后将<Record ID>替换为您之前创建的案例记录的ID。
    {
      "id": "<Record_ID>",
      "status" : "Working",
      "subject" : "Bigfoot Sighting!",
      "priority" : "Medium"
    }

    注意

    ID字段是可选的。要创建案例记录,请忽略此字段。在我们的例子中,你传递这个字段是因为你想更新案例记录。

  4. 点击 Execute.

    该请求从您的REST服务调用upsertCase方法。状态,主题和优先级字段被更新。即使主题的值与旧主题相匹配,主题也会更新。此外,由于请求主体不包含“案例源”字段的值,因此upsertCase方法中的origin参数为null。因此,记录更新时,“原始”字段将被清除。

    要检查这些字段,请通过导航到 https://yourInstance.salesforce.com/<Record ID>中查看此记录。

使用PATCH方法更新数据

作为PUT方法的替代方法,使用PATCH方法更新记录字段。你可以用不同的方式实现PATCH方法。一种方法是在方法中指定参数以更新每个字段。例如,您可以创建一个方法来更新具有以下签名的案例的优先级:updateCasePriority(String priority)。要更新多个字段,可以列出所有需要的字段作为参数。

提供更多灵活性的另一种方法是将字段作为请求主体中的JSON名称/值对传递。这种方法可以接受任意数量的参数,并且参数在方法的签名中不固定。这种方法的另一个优点是没有字段被意外清除,因为是空的。您添加到CaseManager类的updateCaseFields方法使用第二种方法。此方法将请求正文中的JSON字符串反序列化为名称/值对映射,并使用sObject PUT方法设置字段。

@HttpPatch
global static ID updateCaseFields() {
    RestRequest request = RestContext.request;
    String caseId = request.requestURI.substring(
        request.requestURI.lastIndexOf('/')+1);
    Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
    // 将JSON字符串反序列化为名称 - 值对
    Map<String, Object> params = (Map<String, Object>)JSON.deserializeUntyped(request.requestbody.tostring());
    // 遍历每个参数字段和值
    for(String fieldName : params.keySet()) {
        // 设置Case sObject的字段和值
        thisCase.put(fieldName, params.get(fieldName));
    }
    update thisCase;
    return thisCase.Id;
}
要调用PATCH方法:

  1. 在Workbench REST Explorer中,单击 PATCH.
  2. 对于URI,输入 /services/apexrest/Cases/<Record ID>. 将<Record ID> 替换为先前创建的案例记录的ID。在请求正文中输入以下JSON。
    {
      "status" : "Escalated",
      "priority" : "High"
    }
    这个JSON有两个字段值:状态和优先级。 updateCaseFields方法从提交的JSON中检索这些值,并用于指定要在对象中更新的字段。
  3. 点击 Execute.

    该请求调用REST服务中的updateCaseFields方法。案例记录的状态和优先级字段被更新为新值。要检查这些字段,请通过导航到https://yourInstance.salesforce.com/<Record ID>在Salesforce中查看此记录。

测试您的Apex REST类

测试您的Apex REST类与测试其他Apex类相似,只需传入参数值即可调用类方法,然后验证结果。对于不接受参数或依赖REST请求中的信息的方法,创建一个测试REST请求。
一般来说,以下是测试Apex REST服务的方法。要模拟REST请求,请在测试方法中创建RestRequest,然后按如下方式在请求上设置属性。您也可以在请求中添加“通过”的参数来模拟URI参数。
// 设置一个测试请求
RestRequest request = new RestRequest();

// 设置请求属性
request.requestUri =
    'https://yourInstance.salesforce.com/services/apexrest/Cases/'
    + recordId;
request.httpMethod = 'GET';

// 设置其他属性,如参数
request.params.put('status', 'Working');

// 更令人敬畏的代码在这里...
// 最后,如果使用,将请求分配给RestContext
RestContext.request = request;
如果您正在测试的方法通过RestContext访问请求值,则将请求分配给RestContext以填充它(RestContext.request = request;)。

现在,让我们将整个课程保存在Developer Console中,然后运行结果。

  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于类名称,输入CaseManagerTest,然后单击 OK.
  3. 将自动生成的代码替换为以下类定义。
    @IsTest
    private class CaseManagerTest {
    
        @isTest static void testGetCaseById() {
            Id recordId = createTestRecord();
            // 设置一个测试请求
            RestRequest request = new RestRequest();
            request.requestUri =
                'https://yourInstance.salesforce.com/services/apexrest/Cases/'
                + recordId;
            request.httpMethod = 'GET';
            RestContext.request = request;
            // 调用测试方法
            Case thisCase = CaseManager.getCaseById();
            // 验证结果
            System.assert(thisCase != null);
            System.assertEquals('Test record', thisCase.Subject);
        }
    
        @isTest static void testCreateCase() {
            // 调用测试方法
            ID thisCaseId = CaseManager.createCase(
                'Ferocious chipmunk', 'New', 'Phone', 'Low');
            // 验证结果
            System.assert(thisCaseId != null);
            Case thisCase = [SELECT Id,Subject FROM Case WHERE Id=:thisCaseId];
            System.assert(thisCase != null);
            System.assertEquals(thisCase.Subject, 'Ferocious chipmunk');
        }
    
        @isTest static void testDeleteCase() {
            Id recordId = createTestRecord();
            // 设置一个测试请求
            RestRequest request = new RestRequest();
            request.requestUri =
                'https://yourInstance.salesforce.com/services/apexrest/Cases/'
                + recordId;
            request.httpMethod = 'GET';
            RestContext.request = request;
            // 调用测试方法
            CaseManager.deleteCase();
            // 验证记录被删除
            List<Case> cases = [SELECT Id FROM Case WHERE Id=:recordId];
            System.assert(cases.size() == 0);
        }
    
        @isTest static void testUpsertCase() {
            // 1.插入新记录
            ID case1Id = CaseManager.upsertCase(
                    'Ferocious chipmunk', 'New', 'Phone', 'Low', null);
            // 验证新记录是否已创建
            System.assert(Case1Id != null);
            Case case1 = [SELECT Id,Subject FROM Case WHERE Id=:case1Id];
            System.assert(case1 != null);
            System.assertEquals(case1.Subject, 'Ferocious chipmunk');
            // 2. 将现有记录的状态更新为Working
            ID case2Id = CaseManager.upsertCase(
                    'Ferocious chipmunk', 'Working', 'Phone', 'Low', case1Id);
            // 验证记录已更新
            System.assertEquals(case1Id, case2Id);
            Case case2 = [SELECT Id,Status FROM Case WHERE Id=:case2Id];
            System.assert(case2 != null);
            System.assertEquals(case2.Status, 'Working');
        }    
    
        @isTest static void testUpdateCaseFields() {
            Id recordId = createTestRecord();
            RestRequest request = new RestRequest();
            request.requestUri =
                'https://yourInstance.salesforce.com/services/apexrest/Cases/'
                + recordId;
            request.httpMethod = 'PATCH';
            request.addHeader('Content-Type', 'application/json');
            request.requestBody = Blob.valueOf('{"status": "Working"}');
            RestContext.request = request;
            // 将现有记录的状态更新为Working
            ID thisCaseId = CaseManager.updateCaseFields();
            // 验证记录已更新
            System.assert(thisCaseId != null);
            Case thisCase = [SELECT Id,Status FROM Case WHERE Id=:thisCaseId];
            System.assert(thisCase != null);
            System.assertEquals(thisCase.Status, 'Working');
        }  
    
        // Helper method
        static Id createTestRecord() {
            // Create test record
            Case caseTest = new Case(
                Subject='Test record',
                Status='New',
                Origin='Phone',
                Priority='Medium');
            insert caseTest;
            return caseTest.Id;
        }          
    
    }
  4. 按下CTRL + S保存。
  5. 通过选择测试|运行您的组织中的所有 Test | Run All.

测试结果显示在“测试”选项卡中。测试执行完成后,请检查“总体代码覆盖率”窗格中的CaseManager行。这是在100%的覆盖面。

告诉我更多…

了解Apex REST,Salesforce API和安全注意事项中支持的数据类型和名称空间。
Apex REST支持的数据类型
Apex REST支持这些数据类型的参数和返回值。

  • Apex基元(不包括sObject和Blob)。
  • sObjects
  • Apex基元或sObjects的列表或映射(仅支持使用String键的映射)。
  • 用户定义的类型包含上面列出的类型的成员变量。
Apex REST端点中的命名空间
Apex REST端点中的命名空间
Apex REST方法可用于托管和非托管包中。调用托管包中包含的Apex REST方法时,需要将托管包名称空间包含在REST调用URL中。例如,如果该类包含在名为packageNamespace的托管包名称空间中,并且Apex REST方法使用/ MyMethod / *的URL映射,则通过REST调用这些方法的URL将采用https:// instance形式。 salesforce.com/services/apexrest/packageNamespace/MyMethod/。
自定义Apex Web服务和Salesforce API
外部应用程序可以使用Salesforce的REST和SOAP API与Salesforce集成,而不是使用REST和SOAP服务的自定义Apex代码。这些API使您可以创建,更新和删除记录。但是,使用Apex Web服务的优势在于Apex方法可以封装复杂的逻辑。这个逻辑对消费应用程序是隐藏的。此外,Apex类操作可能比单独调用API更快,因为在客户端和Salesforce服务器之间执行的往返次数较少。通过Apex Web服务调用,仅发送一个请求,并且方法中的所有操作都在服务器上执行。
Apex Web服务的安全注意事项
Apex Web服务方法运行的安全上下文与Salesforce API的安全上下文不同。与Salesforce API不同,Apex Web服务方法以系统权限运行,并且不尊重用户的对象和字段权限。但是,Apex Web服务方法在使用with sharing关键字声明时会强制执行共享规则。

Salesforce API(8)SOAP外调

学习目标

完成本单元后,您将能够:

  • 使用WSDL2Apex生成Apex类。
  • 执行标注以使用SOAP将数据发送到外部服务。
  • 使用模拟标注测试标注。

使用WSDL2Apex生成Apex代码

除了REST标注外,Apex还可以使用XML对SOAP Web服务进行标注。使用SOAP可能是一个痛苦的(但必要的)经验。幸运的是,我们有工具来简化这个过程。
WSDL2Apex从WSDL文档自动生成Apex类。您下载Web服务的WSDL文件,然后上传WSDL,WSDL2Apex为您生成Apex类。 Apex类构造SOAP XML,传输数据,并将响应XML解析到Apex对象中。而不是开发构建和分析Web服务消息的XML的逻辑,让由WSDL2Apex生成的Apex类在内部处理所有这些开销。如果您熟悉WSDL2Java或使用.NET作为Web引用导入WSDL,则此功能类似于WSDL2Apex。别客气。

注意

尽可能使用出站消息处理集成解决方案。仅在必要时才使用对第三方Web服务的标注。

对于这个例子,我们使用一个简单的计算器Web服务来添加两个数字。这是一个开创性的服务,是所有的愤怒!我们需要做的第一件事是下载WSDL文件来生成Apex类。点击此链接并将calculator.xml文件下载到您的计算机上。记住你保存这个文件的位置,因为你在下一步需要它。

从WSDL生成一个Apex类

  1. 从“设置”中,在“快速查找”框中输入“Apex类”,然后单击 Apex Classes.
  2. 点击 Generate from WSDL.
  3. 点击 Choose File 然后选择下载的calculator.xml文件。
  4. 点击 Parse WSDL. 应用程序为WSDL文档中的每个名称空间生成默认的类名称,并报告任何错误。

    对于这个例子,使用默认的类名称。但是,在现实生活中,强烈建议您更改默认名称,以使其更易于使用,并使代码更直观。

    现在该坦诚地谈谈WSDL解析器了。 WSDL2Apex解析是一个臭名昭着的善变。解析过程可能由于多种原因而失败,例如不支持的类型,多个绑定或未知元素。不幸的是,您可能会被迫手动编写调用Web服务的Apex类或使用HTTP。

  5. 点击 Generate Apex code. 向导的最后一页显示生成的类以及任何错误。该页面还提供链接以查看成功生成的代码。
    生成的Apex类包括用于调用由WSDL文档表示的第三方Web服务的存根类和类类。这些类允许您从Apex调用外部Web服务。对于每个生成的类,使用相同的名称和前缀Async创建第二个类。 calculatorServices类用于同步标注。 AsyncCalculatorServices类用于异步标注。

执行标注

先决条件

在运行此示例之前,请使用“授权端点地址”部分中的步骤来授权Web服务标注的端点URL https://th-apex-soap-service.herokuapp.com

现在你可以执行标注,看看它是否正确添加两个数字。有一个计算器,方便检查结果。

  1. 从设置档(打开设备齿轮图标Setup gear icon)打开开发者控制台。
  2. 在开发者控制台中,选择 Debug | Open Execute Anonymous Window.
  3. 删除所有现有的代码,并插入下面的代码片段。
    calculatorServices.CalculatorImplPort calculator = new  calculatorServices.CalculatorImplPort();
    Double x = 1.0;
    Double y = 2.0;
    Double result = calculator.doAdd(x,y);
    System.debug(result);
  4. 选择 Open Log, 然后单击 Execute.
  5. 调试日志打开后,单击调试只查看System.debug语句的输出。日志应该显示3.0。

测试Web服务标注

所有有经验的Apex开发人员都知道,要部署或打包Apex代码,至少有75%的代码必须具有测试覆盖率。这个覆盖范围包括我们由WSDL2Apex生成的类。您以前可能听说过这个,但测试方法不支持Web服务标注,执行Web服务标注的测试失败。所以,我们有一点工作要做。为了防止测试失败并增加代码覆盖率,Apex提供了一个内置的WebServiceMock接口和Test.setMock方法。您可以使用此接口在测试方法中接收假响应,从而提供必要的测试覆盖率。

为标注指定模拟响应

当您从WSDL创建Apex类时,自动生成的类中的方法调用WebServiceCallout.invoke,该方法执行对外部服务的调用。在测试这些方法时,只要调用WebServiceCallout.invoke,就可以指示Apex运行时生成一个假响应。为此,请实现WebServiceMock接口并指定测试运行时发送的假响应。

指示Apex运行时通过在您的测试方法中调用Test.setMock来发送此假响应。对于第一个参数,传递WebServiceMock.class。对于第二个参数,传递一个WebServiceMock接口实现的新实例。

Test.setMock(WebServiceMock.class, new MyWebServiceMockImpl());
这很重要,所以让我们来看一个完整的示例代码。在这个例子中,你创建了一个名为callout的类,一个用于测试的模拟实现和一个测试类本身。
  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于类名称,输入AwesomeCalculator,然后单击 OK.
  3. 用下面的类定义替换自动生成的代码。
    public class AwesomeCalculator {
        public static Double add(Double x, Double y) {
            calculatorServices.CalculatorImplPort calculator = 
                new calculatorServices.CalculatorImplPort();
            return calculator.doAdd(x,y);
        }
    }
  4. 按下CTRL + S保存。

    在测试过程中创建您的模拟实现来伪造标注。您的WebServiceMock实现调用doInvoke方法,该方法返回您为测试指定的响应。这些代码大部分是样板。这个练习中最难的部分就是搞清楚Web服务如何返回一个响应,这样你就可以伪造一个值。

  5. 在开发者控制台中,选择 File | New | Apex Class.
  6. 对于类名称,输入CalculatorCalloutMock,然后单击 OK.
  7. 将自动生成的代码替换为以下类定义。
    @isTest
    global class CalculatorCalloutMock implements WebServiceMock {
       global void doInvoke(
               Object stub,
               Object request,
               Map<String, Object> response,
               String endpoint,
               String soapAction,
               String requestName,
               String responseNS,
               String responseName,
               String responseType) {
            // 开始 - 指定你想发送的响应
            calculatorServices.doAddResponse response_x = 
                new calculatorServices.doAddResponse();
            response_x.return_x = 3.0;
            // 结束
            response.put('response_x', response_x); 
       }
    }
  8. 按下CTRL + S保存。

    最后,您的测试方法需要通过调用Test.setMock来指示Apex运行时发送假响应,然后在AwesomeCalculator类中进行标注。像任何其他测试方法一样,我们断言我们的模拟响应得到了正确的结果。

  9. 在开发者控制台中,选择 File | New | Apex Class.
  10. 对于类名称,输入AwesomeCalculatorTest,然后单击 OK.
  11. 将自动生成的代码替换为以下类定义。
    @isTest
    private class AwesomeCalculatorTest {
        @isTest static void testCallout() {              
            // 这将导致生成一个假的响应
            Test.setMock(WebServiceMock.class, new CalculatorCalloutMock());
            // 调用调用标注的方法
            Double x = 1.0;
            Double y = 2.0;
            Double result = AwesomeCalculator.add(x, y);
            // 验证是否返回假结果
            System.assertEquals(3.0, result); 
        }
    }
  12. 按下CTRL + S保存。
  13. 要运行测试,请选择 Test | Run All.

AwesomeCalculator类现在应该显示100%的代码覆盖率!

Salesforce API(7)REST外调

学习目标

完成本单元后,您将能够:

  • 执行标注以接收来自外部服务的数据。
  • 执行标注将数据发送到外部服务。
  • 使用模拟标注测试标注。

HTTP和标注基础

REST标注基于HTTP。要了解标注如何工作,了解一些有关HTTP的内容很有帮助。每个标注请求都与HTTP方法和端点相关联。 HTTP方法指示需要什么类型的动作。
Apex callouts to an external service

最简单的请求是一个GET请求(GET是一个HTTP方法)。 GET请求意味着发送者想要从服务器获取关于资源的信息。当服务器接收并处理此请求时,它将请求信息返回给收件人。 GET请求与导航到浏览器中的地址相似。当您访问网页时,浏览器会在幕后执行GET请求。在浏览器中,导航的结果是显示的新HTML页面。用标注,结果是响应对象。

为了说明GET请求的工作方式,打开浏览器并导航到以下URI: https://th-apex-http-callout.herokuapp.com/animals. 您的浏览器以奇怪的格式显示动物列表,因为服务以JSON格式返回响应。有时GET响应也是用XML格式化的。

以下是常用HTTP方法的说明。

表1.一些常用的HTTP方法
HTTP方法 描述
GET 检索由URL标识的数据。
POST 创建资源或发布数据到服务器。
DELETE 删除由URL标识的资源。
PUT 创建或替换请求正文中发送的资源。

如果您有空闲时间,请在参考资料部分浏览所有HTTP方法的详尽列表。

除了HTTP方法之外,每个请求都会设置一个URI,这是服务所在的端点地址。例如,一个端点可以是http://www.example.com/api/resource。在“HTTP和标注基础”单元中的示例中,端点是https://th-apex-http-callout.herokuapp.com/animals。

当服务器处理请求时,它在响应中发送一个状态码。状态码指示请求是否成功处理或是否遇到错误。如果请求成功,服务器会发送一个200的状态码。您可能已经看到一些其他的状态码,例如404没有找到文件,500没有找到内部服务器错误。

如果在浏览HTTP方法列表后仍然有空闲时间,请查看参考资料部分的所有响应状态代码列表。如果你晚上睡得很困难,这两个资源可以帮助你。

注意

除了端点和HTTP方法之外,还可以为请求设置其他属性。例如,请求可以包含提供有关请求的更多信息的标题,例如内容类型。请求还可以包含要发送到服务的数据,例如POST请求。您将在稍后的步骤中看到POST请求的示例。 POST请求类似于单击网页上的按钮来提交表单数据。使用标注,您可以将数据作为请求正文的一部分发送,而不是在网页上手动输入数据。

从服务获取数据

现在是时候把你的新的HTTP知识与一些Apex标注一起使用了。本示例向Web服务发送GET请求以获取林地生物列表。该服务以JSON格式发送响应。 JSON本质上是一个字符串,所以内置的JSONParser类将它转换为一个对象。然后,我们可以使用该对象将每个动物的名称写入调试日志。
在本机运行示例之前,您需要使用“授权端点地址”部分中的步骤来授权标注的端点URL https://th-apex-http-callout.herokuapp.com。
  1. 从设置档(打开设备齿轮图标Setup gear icon)打开开发者控制台。 
  2. 在开发者控制台中,选择 Debug | Open Execute Anonymous Window.
  3. 删除现有的代码并插入下面的代码片段。
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
    request.setMethod('GET');
    HttpResponse response = http.send(request);
    // 如果请求成功,则解析JSON响应。
    if (response.getStatusCode() == 200) {
        // 将JSON字符串反序列化为原始数据类型的集合。
        Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
        // 将“动物”键中的值作为列表进行投射
        List<Object> animals = (List<Object>) results.get('animals');
        System.debug('Received the following animals:');
        for (Object animal: animals) {
            System.debug(animal);
        }
    }
  4. 选择 Open Log,然后单击 Execute.
  5. 调试日志打开后,选择Debug Only以查看System.debug语句的输出。
    显示动物的名字。

我们例子中的JSON相当简单并且易于解析。对于更复杂的JSON结构,可以使用JSON2Apex。该工具生成用于解析JSON结构的强类型的Apex代码。您只需粘贴JSON,该工具就会为您生成必要的Apex代码。辉煌!

发送数据到服务

HTTP标注的另一个常见用例是将数据发送到服务。例如,当您购买最新的贾斯汀·比伯(Justin Bieber)专辑或评论您最喜欢的“鲨鱼服装中的猫在骑着Roomba的同时追逐鸭子”视频时,您的浏览器正在提交POST请求以提交数据。我们来看看我们如何在Apex中发送数据。
本示例向Web服务发送POST请求以添加动物名称。新名称作为JSON字符串添加到请求正文中。请求Content-Type头设置为让服务知道发送的数据是JSON格式,以便它可以适当地处理数据。该服务通过发送状态代码和所有动物列表(包括您添加的动物)作出响应。如果请求已成功处理,则状态码将返回201,因为已创建资源。如果返回201以外的任何内容,则将响应发送到调试日志。
  1. 从设置档(打开设备齿轮图标Setup gear icon)打开开发者控制台。
  2. 在开发者控制台中,选择 Debug | Open Execute Anonymous Window.
  3. 删除任何现有的代码并插入以下片段。
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
    request.setMethod('POST');
    request.setHeader('Content-Type', 'application/json;charset=UTF-8');
    // 将主体设置为JSON对象
    request.setBody('{"name":"mighty moose"}');
    HttpResponse response = http.send(request);
    // 解析JSON响应
    if (response.getStatusCode() != 201) {
        System.debug('The status code returned was not expected: ' +
            response.getStatusCode() + ' ' + response.getStatus());
    } else {
        System.debug(response.getBody());
    }
  4. 选择 Open Log, 然后单击 Execute.
  5. 调试日志打开时,请选择仅调试以查看System.debug语句的输出。动物列表中的最后一项是“mighty moose”.

测试标注

标注测试有好消息和坏消息。坏消息是Apex测试方法不支持标注,执行标注的测试失败。好消息是测试运行时允许你“嘲笑”标注。模拟标注允许您指定在测试中返回的响应,而不是实际调用Web服务。你实际上在告诉运行时,“我知道这个web服务将会返回什么,所以不要在测试过程中调用它,而是返回这个数据。”在你的测试中使用模拟标注有助于确保你获得足够的代码覆盖率,代码由于标注而被跳过。

先决条件

在编写测试之前,让我们创建一个类,其中包含我们在“将数据发送到服务”单元中匿名执行的GET和POST请求示例。这些示例稍有修改,以便请求在方法和返回值中,但它们与前面的示例基本相同。

  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于课程名称,请输入AnimalsCallouts,然后单击 OK.
  3. 将自动生成的代码替换为以下类定义。
    public class AnimalsCallouts {
    
        public static HttpResponse makeGetCallout() {
            Http http = new Http();
            HttpRequest request = new HttpRequest();
            request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
            request.setMethod('GET');
            HttpResponse response = http.send(request);
            // 如果请求成功,则解析JSON响应。
            if (response.getStatusCode() == 200) {
                // 将JSON字符串反序列化为原始数据类型的集合。
                Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
                // 将“动物”键中的值作为列表进行投射
                List<Object> animals = (List<Object>) results.get('animals');
                System.debug('Received the following animals:');
                for (Object animal: animals) {
                    System.debug(animal);
                }
            }
            return response;
        }
    
        public static HttpResponse makePostCallout() {
            Http http = new Http();
            HttpRequest request = new HttpRequest();
            request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
            request.setMethod('POST');
            request.setHeader('Content-Type', 'application/json;charset=UTF-8');
            request.setBody('{"name":"mighty moose"}');
            HttpResponse response = http.send(request);
            // 解析JSON响应
            if (response.getStatusCode() != 201) {
                System.debug('The status code returned was not expected: ' +
                    response.getStatusCode() + ' ' + response.getStatus());
            } else {
                System.debug(response.getBody());
            }
            return response;
        }        
    
    }
  4. 按下CTRL + S保存。

使用StaticResourceCalloutMock测试标注

要测试您的标注,请通过实现接口或使用静态资源来使用模拟标注。在这个例子中,我们稍后使用静态资源和模拟接口。静态资源包含要返回的响应主体。同样,使用模拟标注时,请求不会发送到端点。相反,Apex运行时知道查找在静态资源中指定的响应,并返回它。 Test.setMock方法通知运行时在测试方法中使用模拟标注。让我们看看模拟标注在行动。首先,我们创建一个包含JSON格式字符串的静态资源,用于GET请求。

  1. 在开发者控制台中,选择 File | New | Static Resource.
  2. 对于名称,输入GetAnimalResource。
  3. 对于MIME类型,即使我们使用JSON,也请选择text / plain。
  4. 点击 Submit.
  5. 在为资源打开的选项卡中,插入以下内容。确保它全部在一行上,不会中断到下一行。这个内容是模拟标注返回的内容。这是三个林地生物阵列。
    {"animals": ["pesky porcupine", "hungry hippo", "squeaky squirrel"]}
  6. 按下CTRL + S保存。

您已成功创建您的静态资源!现在,让我们为使用此资源的标注添加一个测试。

  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于类名称,输入AnimalsCalloutsTest,然后单击 OK.
  3. 将自动生成的代码替换为以下测试类定义。
    @isTest
    private class AnimalsCalloutsTest {
    
        @isTest static  void testGetCallout() {
            // 基于静态资源创建模拟响应
            StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
            mock.setStaticResource('GetAnimalResource');
            mock.setStatusCode(200);
            mock.setHeader('Content-Type', 'application/json;charset=UTF-8');
            // 将标注与模拟响应相关联
            Test.setMock(HttpCalloutMock.class, mock);
            // 调用方法来测试
            HttpResponse result = AnimalsCallouts.makeGetCallout();
            // 验证模拟响应不为空
            System.assertNotEquals(null,result,
                'The callout returned a null response.');
            // 验证状态码
            System.assertEquals(200,result.getStatusCode(),
              'The status code is not 200.');
            // 验证内容类型
            System.assertEquals('application/json;charset=UTF-8',
              result.getHeader('Content-Type'),
              'The content type value is not expected.');  
            // 验证数组包含3个项目 
            Map<String, Object> results = (Map<String, Object>) 
                JSON.deserializeUntyped(result.getBody());
            List<Object> animals = (List<Object>) results.get('animals');
            System.assertEquals(3, animals.size(),
              'The array should only contain 3 items.');          
        }   
    
    }
  4. 按下CTRL + S保存。
  5. 选择 Test | Always Run Asynchronously.如果不选择“始终运行异步”,则测试运行只包含一个同步运行的类。您只能从“测试”选项卡打开日志,以进行同步测试运行。
  6. 要运行测试,请选择 Test | New Run.
  7. 从Test Classes列表中选择 AnimalsCalloutsTest.
  8. 点击 Add Selected | Run.

测试结果显示在测试运行ID下的测试选项卡中。测试执行完成后,展开测试运行以查看详细信息。现在在“代码总体覆盖率”窗格中双击AnimalCallouts,查看测试覆盖了哪些行。

用HttpCalloutMock测试标注

为了测试你的POST标注,我们提供了一个HttpCalloutMock接口的实现。此接口使您能够指定在响应方法中发送的响应。您的测试类指示Apex运行时通过再次调用Test.setMock来发送此假响应。对于第一个参数,传递HttpCalloutMock.class。对于第二个参数,传递一个AnimalsHttpCalloutMock的新实例,它是您的HttpCalloutMock的接口实现。 (在这个例子之后的例子中,我们将写AnimalsHttpCalloutMock)

Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());
现在添加实现HttpCalloutMock接口的类来拦截标注。如果在测试上下文中调用HTTP标注,则不会进行标注。相反,您会收到您在AnimalsHttpCalloutMock中的响应方法实现中指定的模拟响应。
  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于类名,输入AnimalsHttpCalloutMock,然后单击 OK.
  3. 将自动生成的代码替换为以下类定义。
    @isTest
    global class AnimalsHttpCalloutMock implements HttpCalloutMock {
        // 实现这个接口方法
        global HTTPResponse respond(HTTPRequest request) {
            // 创建一个假的回应
            HttpResponse response = new HttpResponse();
            response.setHeader('Content-Type', 'application/json');
            response.setBody('{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}');
            response.setStatusCode(200);
            return response; 
        }
    }
  4. 按下CTRL + S保存。

在您的测试类中,创建testPostCallout方法来设置模拟标注,如下例所示。 testPostCallout方法调用Test.setMock,然后调用AnimalsCallouts类中的makePostCallout方法。然后验证返回的响应是您在模拟实现的响应方法中指定的。

  1. 修改测试类AnimalsCalloutsTest添加第二个测试方法。单击类选项卡,然后在右括号之前添加以下方法。
    @isTest static void testPostCallout() {
        // 设置模拟标注类
        Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock()); 
        //这会导致从实现HttpCalloutMock的类发送假响应。 
        HttpResponse response = AnimalsCallouts.makePostCallout();
        // Verify that the response received contains fake values
        String contentType = response.getHeader('Content-Type');
        System.assert(contentType == 'application/json');
        String actualValue = response.getBody();
        System.debug(response.getBody());
        String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}';
        System.assertEquals(actualValue, expectedValue);
        System.assertEquals(200, response.getStatusCode());
    }
  2. 按下CTRL + S保存。
  3. 选择 Test | New Run.
  4. 从Test Classes列表中选择 AnimalsCalloutsTest.
  5. 点击 Add Selected | Run.

    测试结果显示在“测试”选项卡下的新测试运行ID下。当测试执行完成时,展开测试运行以查看有关这两个测试的详细信息。

告诉我更多…

了解如何在触发器和异步Apex中使用标注,以及如何制作异步标注。
当从一个方法进行标注时,该方法在执行后续代码行之前,等待外部服务发回标注响应。或者,您可以将标注代码放置在使用@future(callout = true)标注的异步方法中,或使用Queueable Apex。这样,标注在单独的线程上运行,调用方法的执行不会被阻止。

从触发器进行标注时,标注不得在等待响应时阻止触发过程。为了使触发器能够进行标注,包含标注代码的方法必须使用@future(callout = true)进行注释以在单独的线程中运行。

Salesforce API(6)集成

学习目标

完成本单元后,您将能够:

  • 描述Web服务和HTTP标注之间的差异。
  • 使用远程站点设置授权外部站点。

从Apex向外部服务进行标注

Apex标注使您能够将Apex代码与外部服务紧密集成。标注调用外部Web服务或从Apex代码发送HTTP请求,然后接收响应。
Apex标注有两种口味。
  • SOAP Web服务的Web服务调用使用XML,通常需要WSDL文档来生成代码。
  • 服务的HTTP标注通常使用REST和JSON。

这两种类型的标注在向服务发送请求和接收响应方面是相似的。但是,虽然基于WSDL的标注适用于SOAP Web服务,但HTTP标注可以与任何HTTP服务(SOAP或REST)一起使用。

所以你现在可能正在问自己:“我该用哪一个?”尽可能使用HTTP服务。这些服务通常更容易交互,需要更少的代码,并且使用易读的JSON。在过去的几年中,所有“酷儿”都已经转向REST服务,但这并不是说SOAP Web服务是不好的。他们已经永远(在互联网年),通常用于企业应用程序。他们不会很快离开。当与传统应用程序集成或需要正式交换格式或有状态操作的事务时,您可能主要使用SOAP。在这个模块中,我们将介绍SOAP,但是我们将大部分时间都花在REST上。

授权端点地址

我们热爱Salesforce的安全!所以,任何时候您都可以向外部网站发送宣传信息,我们希望确保其已被授权。未经事先批准,我们不能将代码无条件地发送给任何端点。在开始使用标注之前,请在“远程站点设置”页面上更新您的组织的已批准站点的列表。
我们将在这个模块中使用以下端点,现在继续添加它们。如果您忘记添加终端,请相信我,当您尝试运行您的代码时,您会收到提醒。我们会打电话给以下网站。
  • https://th-apex-http-callout.herokuapp.com
  • https://th-apex-soap-service.herokuapp.com

按照以下步骤授权这两个端点URL。

  1. 从“设置”中,在“快速查找”框中输入 Remote Site Settings然后单击 Remote Site Settings.
  2. 单击 New Remote Site.
  3. 对于远程站点名称,输入 animals_http.
  4. 对于远程站点URL,请输入 https://th-apex-http-callout.herokuapp.com. 该URL授权端点的所有子文件夹,如https://th-apex-http-callout.herokuapp.com/path1和 https://th-apex-http-callout.herokuapp.com/path2.
  5. 对于描述,请输入 Trailhead animal service: HTTP.
  6. 点击 Save & New.
  7. 对于第二个远程站点名称,输入 animals_soap.
  8. 对于远程站点URL,请输入 https://th-apex-soap-service.herokuapp.com.
  9. 有关说明,请输入 Trailhead animal service: SOAP.
  10. 点击 Save.

Salesforce API(5)Streaming

学习目标

完成本单元后,您将能够:

  • 描述推技术提供的过拉技术的主要好处。
  • 创建一个PushTopic并接收事件通知。
  • 指定重播选项以进行持续流式传输。
  • 用通用流播放消息。

流事件

为了总结我们对Salesforce数据API的调查,我们来看看一个完全不同用例的API。流式API使您可以根据您定义的条件将一系列通知从Salesforce推送到客户端应用程序。推送通知与我们其他API使用的拉式范例有什么不同,客户端应用程序向Salesforce请求还是从中拉取数据?我们从船长的角度来看问题。
想象一下,你正在航行公海,你要留意迎面而来的危险,其他船只和富有宝藏的岛屿。你把一个水手放在鱼窝里保持积极的了望。现在再次戴上你的开发者帽子。假设您正在使用REST或SOAP API编写应用程序,该应用程序会定期检查是否有帐户已更新。您可以使用类似的解决方案,并通过不断请求帐户数据并检查其是否与旧数据匹配来保持主动了望。

现在想象你再次在你的船上,但是这次你可以使用一个闪亮的新型雷达显示器。你不需要把水手留在鱼窝里,因为每当感兴趣的物体接近时,显示器会发出蜂鸣声。

流媒体API是你的雷达。它允许您定义事件并在事件发生时将通知推送到您的客户端应用程序。您不必积极监视数据更改 – 您不必经常轮询Salesforce并发出不必要的API请求。

Streaming API can be used like a radar to detect data changes and send notifications

在Salesforce外部系统中存储业务数据时,跟踪Salesforce中的数据更改尤其有用。您可以使用Streaming API将您的外部来源与Salesforce数据保持同步。此外,Streaming API还可以处理外部系统中的业务逻辑,以响应Salesforce中的数据更改。例如,您可以使用Streaming API在机会更新时通知履行中心。

除了数据更改之外,还可以使用Streaming API为Salesforce外部定义的事件广播通知。例如,您可以在系统维护时段即将开始时或向用户提供新的优惠时使您的应用程序显示消息。这个功能被称为通用流媒体,我们也会介绍它。

Streaming API使用Bayeux协议和CometD消息库。

PushTopics

我们通过PushTopics与Streaming API进行交互。 PushTopic是一个sObject,它包含您想要侦听的事件的标准,例如特定对象的数据更改。您可以在PushTopic中将条件定义为SOQL查询,并指定记录操作以通知(创建,更新,删除和取消删除)。除了事件标准之外,PushTopic表示客户端应用程序订阅的频道。
当我们创建我们自己的PushTopic时,我们会深入下去。

PushTopic查询中支持的对象

PushTopic查询支持所有自定义对象。 PushTopic查询支持以下标准对象。

  • Account
  • Campaign
  • Case
  • Contact
  • Lead
  • Opportunity
  • Task

通过试验程序在PushTopic查询中支持以下标准对象。

  • ContractLineItem
  • Entitlement
  • LiveChatTranscript
  • Quote
  • QuoteLineItem
  • ServiceContract

PushTopics和通知

PushTopic使您能够定义您感兴趣接收事件通知的对象,字段和条件。以下示例显示了在Apex中定义并插入的PushTopic。在创建PushTopic之后,您可以订阅此PushTopic频道以跟踪账单城市是旧金山的客户的变化。此PushTopic指定在每个事件通知中返回Id,Name,Phone字段。默认情况下,会发送通知来创建,更新,删除和取消删除与查询条件匹配的操作。

PushTopic pushTopic = new PushTopic();
pushTopic.Name = 'AccountUpdates';
pushTopic.Query = 'SELECT Id, Name, Phone FROM Account WHERE BillingCity=\'San Francisco\'';
pushTopic.ApiVersion = 37.0;

insert pushTopic;
至少定义PushTopic名称,查询和API版本。您可以使用其余属性的默认值。默认情况下,SELECT语句字段列表和WHERE子句中的字段是触发通知的字段。通知只发送给符合WHERE子句条件的记录。要更改哪些字段触发通知,请将pushTopic.NotifyForFields设置为其中一个值。
NotifyForFields值 描述
All 如果所评估的记录符合WHERE子句中指定的条件,则会为所有记录字段更改生成通知。
Referenced (default) 评估SELECT和WHERE子句中引用的字段的更改。只有在评估记录符合WHERE子句中指定的条件时,才会生成通知。
Select 计算SELECT子句中引用的字段的更改。只有在评估记录符合WHERE子句中指定的条件时,才会生成通知。
Where 对WHERE子句中引用的字段的更改进行评估。只有在评估记录符合WHERE子句中指定的条件时,才会生成通知。

要显式设置通知首选项,请将以下属性设置为true或false。默认情况下,所有值都设置为true。

pushTopic.NotifyForOperationCreate = true;
pushTopic.NotifyForOperationUpdate = true;
pushTopic.NotifyForOperationUndelete = true;
pushTopic.NotifyForOperationDelete = true;
如果您创建一个帐户,则会生成一个事件通知。通知以JSON形式提供,并包含我们在PushTopic查询中指定的字段:Id,Name和Phone。事件通知类似于以下内容。
{
  "clientId": "lxdl9o32njygi1gj47kgfaga4k", 
  "data": {
    "event": {
      "createdDate": "2016-09-16T19:45:27.454Z", 
      "replayId": 1, 
      "type": "created"
    }, 
    "sobject": {
      "Phone": "(415) 555-1212", 
      "Id": "001D000000KneakIAB", 
      "Name": "Blackbeard"
    }
  }, 
  "channel": "/topic/AccountUpdates"
}
通知消息包含PushTopic的通道,其名称格式为 /topic/PushTopicName. 当您创建一个PushTopic时,通道会自动创建。

推送专题查询

让我们花点时间深入一下我们为我们的PushTopic定义的查询。 PushTopic查询是常规的SOQL查询,所以如果您熟悉SOQL,则不需要学习新的格式。如果你不熟悉,不要担心。我们在这里介绍基本的查询格式。简而言之,查询包含一个带有可选WHERE子句的SELECT语句,如下所示。

SELECT <comma-separated list of fields> FROM <Salesforce object> WHERE <filter criteria>
默认情况下,逗号分隔的字段列表指定要检测其更改的字段。这些字段的值在通知消息中发送。 FROM子句指定我们感兴趣的数据更改的对象。WHERE子句根据条件将记录集合缩小为更具体的集合。如果WHERE子句中的条件得到满足,则对WHERE子句中的字段的更改也会生成通知。您可以通过修改PushTopic字段NotifyForFields来更改哪些字段生成通知。
WHERE子句支持SOQL支持的运算符,如LIKE,IN,OR和AND运算符。例如,要过滤帐单城市是旧金山或纽约的帐户,我们将使用此查询。
SELECT Id, Name, Phone FROM Account WHERE BillingCity='San Francisco' OR BillingCity='New York'
为了确保及时发送通知,以下要求适用于PushTopic查询。
  • SELECT语句的字段列表必须包含Id。
  • 每个查询只允许一个对象。
  • 该对象必须对指定的API版本有效。

某些查询不受支持,例如聚合查询或半连接。

使用持久数据流检索过去的通知

到目前为止,您已经了解了有关Salesforce记录更改的PushTopics和事件通知。如果在客户端订阅PushTopic之前创建或更新Salesforce记录,会发生什么情况?在API版本37.0之前,客户端错过了相应的通知。从API版本37.0起,即使没有人订阅PushTopic,Salesforce也会存储与PushTopic查询匹配的事件。这些事件被存储了24小时,您可以随时在窗口中找回它们。好极了!

从API版本37.0开始,每个事件通知都包含一个名为replayId的字段。与重放视频类似,Streaming API会重播使用replayId字段发送的事件通知。 replayId字段的值是标识流中事件的数字。重播ID对于组织和渠道是唯一的。当您重放一个事件时,您将从存储流中的位置检索存储的事件。您可以检索由重播ID指定的事件之后开始的事件流,也可以检索所有存储的事件。以下是订阅频道时可以指定的重播选项的摘要。

重播选项 描述
replayId 订阅者接收通过重播ID值指定的事件发生的事件的通知。
-1 订阅者接收订阅后发生的所有新事件的通知。
-2 订阅者接收订阅后发生的所有新事件的通知,以及在24小时保留窗口内的先前事件。

此图显示了事件使用者如何使用各种重播选项来读取事件流。

Streaming events with replay options

样品持久的流媒体应用程序

要了解如何订阅事件并使用重播选项,请参阅“Streaming API开发人员指南”中的持久流式码示例。提供了几个示例,包括使用JavaScript CometD扩展的Visualforce页面和使用CometD连接器的Java客户端。这些代码示例的链接包含在参考资料部分。

通用流媒体

在您自行启动之前,让我们花几分钟的时间来回顾一下通用流。 Streaming API支持使用不受Salesforce数据更改限制的通用有效内容发送通知。
您可以在任何想要发送自定义通知的情况下使用通用流式传输,例如:

  • 将消息广播给特定的团队或整个组织
  • 发送Salesforce外部事件的通知

要使用通用流式传输,您需要:

  • 定义频道的流媒体频道
  • 一个或多个客户订阅该频道
  • Streaming Channel Push资源监视和调用通道上的事件

您可以通过用户界面中的Streaming Channels应用程序或通过API为通用流式创建流式通道。流式通道由StreamingChannel sObject表示,因此您可以通过Apex,REST API或SOAP API来创建它。通用流的通道名称的格式是 /u/ChannelName. 例如,这个Apex片段创建一个名为广播的频道。

StreamingChannel ch = new StreamingChannel();
ch.Name = '/u/Broadcast';

insert ch;
或者,您可以选择让Salesforce动态地为您创建流式频道(如果不存在)。要在组织中启用动态流式传输渠道,请从“设置”中,在快速查找框中输入用户界面,然后选择 User Interface. 在“用户界面”页面上,选择 Enable Dynamic Streaming Channel Creation 选项。

您可以使用CometD客户端订阅频道。 (参考资料部分链接到Streaming API开发人员指南中的示例演练。)

要生成事件,请向以下REST资源发出POST请求。用您的频道的ID替换XX.0与API版本和 Streaming Channel ID

/services/data/vXX.0/sobjects/StreamingChannel/Streaming Channel ID/push

注意

要获得您的通道ID,请在StreamingChannel上运行SOQL查询,如: SELECT Id, Name FROM StreamingChannel

示例REST请求正文。

{ 
  "pushEvents": [
      { 
          "payload": "Broadcast message to all subscribers", 
          "userIds": [] 
      } 
   ] 
}

注意

而不是广播给所有用户,通过使用可选的userIds字段指定一个订阅用户列表发送通知。另外,您还可以使用Streaming Channel Push REST API资源的GET方法获取该频道的活动订阅者列表。

订阅的客户端收到的事件通知看起来类似于以下内容。

{
  "clientId": "1p145y6g3x3nmnlodd7v9nhi4k", 
  "data": {
    "payload": "Broadcast message to all subscribers", 
    "event": {
      "createdDate": "2016-09-16T20:43:39.392Z", 
      "replayId": 1
    }
  }, 
  "channel": "/u/Broadcast"
}

请注意,此事件通知包含replayId字段。与PushTopic流式传输一样,通用事件通知也存储了24小时,可以使用API​​版本37.0中的replayId值进行检索。

通过活动通知,您可以放心地在公海启航,前往富有宝藏的岛屿!

Salesforce API(4)Bulk

学习目标

完成本单元后,您将能够:

  • 描述异步请求与同步请求的不同之处。
  • 在Workbench中使用REST Explorer创建批量作业。
  • 通过向作业添加数据将数据导入到您的Salesforce组织。
  • 监控工作的进展。
  • 得到一份工作的结果。

批量API和异步请求

批量API基于REST原则,并针对大量数据集进行了优化。您可以使用它来异步插入,更新,插入或删除多个记录,这意味着您提交一个请求并稍后返回结果。 Salesforce在后台处理请求。
相反,SOAP和REST API使用同步请求,并针对一次更新几条记录的实时客户端应用程序进行了优化。您可以使用这两个API来处理许多记录,但是当数据集包含数十万条记录时,它们不太实用。批量API的异步框架旨在使处理从数千条到数百万条记录的数据变得简单高效。

使用Bulk API最简单的方法是使用CSV文件在Data Loader中处理记录。使用Data Loader,您不必编写自己的客户端应用程序。有时候,独特的需求需要编写一个自定义的应用程序。 Bulk API可以让您将自己的轮子放在您自己的手中,并引导课程走向适合您的解决方案。

对于本机,您使用称为批量API 2.0的较新版本的批量API。如果您想将本单元中学到的东西应用于之前支持的批量API版本,则需要使用不同的资源URI,并创建和管理批次以及作业。有关以前版本的Bulk API的更多信息,请参阅批量API开发人员指南。

将数据导入您的组织

要探索Bulk API,我们将使用Workbench创建一些帐户记录。


创建一个批量作业

批量API是基于REST的,因此我们可以使用Workbench的REST Explorer来制作批量API请求。

  1. 登录到您的Trailhead DE组织并导航到Workbench。
  2. 对于环境,请选择 Production. 对于API版本,请选择可用的最高编号。确保您选择 I agree to the terms of service.
  3. 点击 Login with Salesforce.
  4. 在顶部菜单中,选择 utilities | REST Explorer.

现在我们准备上传我们的数据了。第一步是创造一个工作。作业指定我们正在使用的操作和数据对象的类型。它起着一个桶的作用,我们添加数据进行处理。

我们将使用/ jobs / ingest资源来创建一个工作。此资源也可用于获取当前作业的列表。

为了创建一个工作,我们提交一个POST请求到/ jobs / ingest请求体内的作业属性。因为批量API是基于REST的,所以请求采用熟悉的REST请求形式,包含四个组件:URI,HTTP方法,头文件和正文。正如我们刚刚提到的,方法是POST。

对于URI,请将URI文本框中的文本替换为以下内容:/services/data/XX.0/jobs/ingest,其中XX.0对应于您正在使用的API版本。让我们注意一下这个URI的一些事情。

  • 我们正在使用/ services / data,这是与REST API相同的端点。批量API使用与REST API使用的框架相同的框架,这意味着批量API支持许多相同的功能,例如OAuth认证。
  • 与SOAP API类似,API版本号不包含v前缀。
  • /jobs/ingest 表示我们正在访问创建批量API作业的资源。

对于请求正文,请复制并粘贴以下文本。

{
  "operation" : "insert",
  "object" : "Account",
  "contentType" : "CSV",
  "lineEnding" : "CRLF"
}
这些属性表明我们要对我们提交给作业的数据使用插入操作。我们正在提交帐户数据,并且是CSV格式。批量API支持CSV中的有效内容。

您的REST资源管理器看起来像这样。

Create job request in REST Explorer

点击 Execute 并签出响应。

Create job response

答复包括各种有关工作的属性,其中大部分现在对我们没有用处,因为我们还没有添加数据。虽然我们要注意一些属性。查看作业ID(id)并将其复制到文本文件中。我们用它来将数据添加到作业中,并检查作业的状态。还要检查州属性。创建作业时,立即将其设置为打开状态。这意味着它已经准备好开始接收数据。最后,看看contentUrl属性。该属性显示我们用来加载作业数据的URL。

将数据添加到作业

现在我们可以将帐户数据插入我们的工作。作业的数据作为PUT请求中的一组记录发送到服务器。服务器处理记录集,确定将数据加载到Salesforce中的最佳方式。你所要做的就是上传数据。

在Workbench中创建一个新的请求。将URI文本框中的文本替换为以下内容: /services/data/XX.0/jobs/ingest/jobID/batches. 用您复制的作业ID替换jobID。对于HTTP方法,请选择PUT。

对于这个例子,我们添加一组只有四个客户的记录。通常情况下,您使用Bulk API来添加数千或数百万条记录,但原理是一样的。将以下CSV文本复制到请求正文中。

"Name"
"Sample Bulk API Account 1"
"Sample Bulk API Account 2"
"Sample Bulk API Account 3"
"Sample Bulk API Account 4"
我们正在使用CSV数据,因为这是我们在创建作业时指定的。

单击 Headers 并将Content-Type更改为 text/csv. 你的请求看起来像这样。

Add job data using REST Explorer

点击 Execute.

响应仅包含状态码201(已创建),表示Salesforce已成功接收作业数据。

关闭工作

现在我们已经提交了数据,我们需要让Salesforce知道是时候处理数据了。通过将作业状态从“打开”更改为“上传完成”来完成此操作。
将URI文本框中的文本替换为以下内容: /services/data/XX.0/jobs/ingest/jobID. 同样,将XX替换为您正在使用的API版本,并使用作业ID替换jobID。对于HTTP方法,选择PATCH。点击Headers并将Content-Type设置为application / json。在请求正文中,将文本替换为以下JSON文本。

{
   "state" : "UploadComplete"
}
点击 Execute. 该响应包含作业状态信息。状态属性表示作业状态是UploadComplete。此时,Salesforce开始处理作业。

检查作业的状态

我们已经提交了数据。我们让Salesforce知道我们已经完成上传数据。现在由服务器来处理请求。我们可以通过API或通过Salesforce UI检查作业的状态来监视服务器的进度。在Salesforce中,从“设置”中,在“快速查找”框中输入 Bulk Data Load Jobs. 然后选择批量数据加载作业。您可以在此页面上检查作业的状态。或者,您可以单击工作ID来检查状态并获得该工作的详细结果。

在API中,我们使用 /jobs/ingest/jobID 资源来监视作业。将URI文本框中的文本替换为以下内容: /services/data/XX.0/jobs/ingest/jobID, 并使用通常的替换。对于HTTP方法,请选择GET。

点击 Execute. 你看到这样的东西。

Check the job's status

如果您的状态仍然是UploadComplete而不是JobComplete,则Salesforce仍在处理该作业。别担心,它会在几分钟内处理完毕。同时,当你回来的时候,去对待一杯美酒,再次尝试同样的要求。如果你很幸运,你的工作已经完成,继续取回工作结果,或者随时都可以休息一下。

获得工作结果

一旦作业处于JobComplete状态(或失败状态),我们就可以以成功和失败的处理记录的形式获取结果信息。
我们先看看成功处理的记录。在Workbench的REST资源管理器中,将URI文本框中的文本替换为以下内容:/services/data/XX.0/jobs/ingest/jobID/successfulResults
, 并使用通常的替换。对于HTTP方法,请选择GET。

点击 Execute. 你看到这样的东西。

Check the successful results

Salesforce将返回一个CSV,其中包含已成功处理的作业中的所有记录的列表。在这个模块中,我们创建了多个Account记录。 CSV数据包含创建的记录的记录标识,sf__Created列的值为true。

偶尔有些记录不能被处理。也许这个工作试图创建已经存在的账户记录。也许工作数据缺少一些必填字段。在这些情况下,我们可以向Salesforce询问处理过程中出现错误的记录列表,以及有关错误的更多信息。

返回到REST资源管理器,将URI文本框中的文本替换为以下内容:

/services/data/XX.0/jobs/ingest/jobID/failedResults, 并使用通常的替换。对于HTTP方法,请选择GET。

点击 Execute. 结果看起来像这样。

Check the failed results

Salesforce提供一个CSV,其中包含处理期间遇到错误的记录列表以及记录标识和错误消息。在这种情况下,所有记录都成功插入,所以记录列表是空的。整齐的工作,队长!

Salesforce API(3)SOAP

学习目标

完成本单元后,您将能够:

  • 为您的组织生成一个WSDL文件。
  • 使用SoapUI从WSDL文件创建一个SOAP项目。
  • 使用SOAP API登录您的DE组织。
  • 使用SOAP API创建一个帐户。

企业和合作伙伴WSDL

如果您已经航行了另一个基于SOAP的API的海峡,您知道Web服务描述语言(WSDL)文件基本上是您了解如何使用API​​的地图。它包含进行API调用的绑定,协议和对象。
Salesforce为两种不同的使用情况提供了两个SOAP API WSDL。企业WSDL针对单个Salesforce组织进行了优化。它是强类型的,它反映了你的组织特定的配置,这意味着从两个不同的组织生成的两个企业WSDL文件包含不同的信息。

合作伙伴WSDL针对许多Salesforce组织进行了优化。它是松散的类型,它不会根据组织的特定配置进行更改。

通常,如果您要为单个Salesforce组织编写集成,请使用企业WSDL。对于多个组织,请使用合作伙伴WSDL。

对于这个单元,我们使用企业WSDL来研究SOAP API。第一步是为您的组织生成一个WSDL文件。在您的组织架构中,从快速查找框中输入API,然后选择API。在API WSDL页面上,单击 Generate Enterprise WSDL.

Generate enterprise WSDL

在“生成企业WSDL”页面上,单击“Generate”。生成WSDL时,右键单击页面并将WSDL文件保存在计算机上的某个位置。我们很快就会使用它。

这里有一个关于使用企业WSDL的技巧。企业WSDL反映了您的组织的特定配置,因此无论您何时对您的组织进行元数据更改,都需要重新生成WSDL文件。这样,WSDL文件就不会与您的组织配置不同步。

使用SoapUI创建一个SOAP项目

现在我们有了我们的WSDL文件,我们需要一种方法来提取信息来开始制作SOAP API请求。用网络来说,这个过程被称为使用WSDL。就像一个kraken消耗一艘满载不幸船员的船,Force.com Web服务连接器(WSC)等工具使用WSDL文件。然后这些工具创建类,使您能够使用通用编程语言使用SOAP API发出请求。
对于这个单元,我们使用称为SoapUI的第三方工具来使用我们的企业WSDL文件。 SoapUI是一个免费的开源应用程序,用于测试Web服务。要开始,请从SoapUI网站下载并安装SoapUI OpenSource。只安装SoapUI组件。

SoapUI安装并启动后,从File菜单中选择New SOAP Project。对于项目名称,输入Exploring Salesforce SOAP API。对于初始的WSDL,浏览到您保存企业WSDL文件的位置并选择它。不要更改任何其他选项。你的SoapUI窗口看起来像这样。

Exploring Salesforce SOAP API with SoapUI

点击OK。处理几秒钟后,在屏幕左侧的导航器窗格中将出现Exploring Salesforce SOAP API文件夹。它下面是一个名为SoapBinding的条目,其中包含几个操作。

SoapUI SoapBinding folder

那么我们在这里看什么?每个操作都对应于我们可以创建的SOAP API请求。每个操作的属性都是从WSDL文件中的信息中提取的。每个操作还包含一个示例XML请求,其中包含操作的HTTPS端点和预填充的SOAP消息。

还有一点,Salesforce需要所有连接才能使用TLS 1.1或更高版本。如果您在Java 7中使用SoapUI,则默认情况下不启用TLS 1.1。如果您尝试使用旧版本的TLS连接到Salesforce,则会收到错误消息。好消息是,这是一个非常简单的修复。看看这个方便的博客文章了解更多信息。

现在我们都准备发出SOAP API请求。完全航行,cap’n!

登录您的DE组织

在SoapUI中,向下滚动到login 操作。展开它,然后双击请求1.将出现一个示例SOAP登录请求。

Sample SOAP login request

这是对端点URI(1)的快速分解。

  • https://—指定安全的HTTP。
  • login.salesforce.com—登录请求的顶级域名。
  • /services/Soap—指定我们正在发出SOAP API请求。
  • /c—指定我们正在使用企业WSDL。使用/ u作为伙伴WSDL。
  • /36.0—API版本号。 v前缀缺失,因为某些API在版本号之前包含它,有些则不包含。这种行为只是Salesforce API的一个怪癖。
  • /0DF36000000LHZw—软件包版本号。

我们在这个例子中没有使用托管包,所以我们可以从URI的末尾删除包版本号。现在就去做吧。
SOAP消息(2)包含了我们希望在SOAP消息中找到的所有内容:信封,标题和正文。

LoginScopeHeader元素中的属性涉及自助服务和客户门户用户的身份验证。由于我们不必担心这些值,因此请删除整个<urn:LoginScopeHeader>元素(从<urn:LoginScopeHeader>到</ urn:LoginScopeHeader>中的所有内容)。突出显示窗口中的文本,然后按Delete键。

接下来,看一下消息正文中的<urn:login>元素。这个元素是登录请求的大部分。这是我们提供用户凭据的地方。用你的用户名和密码替换你的DE组织。 Trailhead游乐场组织将需要重置密码。

由于您从Salesforce发出的未知IP地址发出API请求,因此您需要将安全令牌附加到密码末尾。例如,如果您的密码是mypassword并且您的安全令牌是XXXXXXXXXX,请在<urn:password>元素内输入mypasswordXXXXXXXXXX。在注册您的DE组织后,您首次重置密码时,您的安全令牌已通过电子邮件发送给您。如果您之后重置密码,则会通过电子邮件将新安全令牌发送给您。如果找不到,可以在Salesforce中重置。

  1. 从您的个人设置中,在快速查找框中输入重置,然后选择 Reset My Security Token.
  2. 单击 Reset Security Token. 新的安全令牌发送到您的Salesforce个人设置中的电子邮件地址。

你的SOAP消息看起来像这样。

A sample SoapUI login request with our DE org credentials

点击请求窗口左上方的播放按钮(绿色三角形)。这个按钮发送请求,把你的SOAP消息放在一个瓶子里。下面是我们扩展响应时的样子。

SOAP API login response

恭喜,船长。您已成功登录。响应中包含有关您的组织和用户的大量信息。最重要的是,它包含您组织的实例(或自定义域,如果您使用的是我的域)以及将用于提出未来请求的会话ID。

Instance server and session ID are contained in the login response

将实例和会话ID复制到文本文件中。我们在一分钟内使用它们。

由于您的组织实例可能发生更改,因此在开始构建集成时,请不要对引用进行硬编码!而是使用Salesforce功能我的域配置一个自定义域。自定义域不仅可以消除更改实例名称的麻烦,还可以使用它来突出显示品牌,使组织更安全,并使您的登录页面个性化。

创建一个帐户

就像我们使用REST API所做的那样,让我们​​使用SOAP API创建一个帐户。在屏幕左侧的导航窗格中,找到create操作。展开它,然后双击 Request 1.因为创建记录比登录更复杂,所以create()SOAP消息包含更多的元素。大部分元素都包含在请求标题中,其中大部分是可选的。为了简化,我们将删除大部分标题信息,但请记住,在创建记录时,这些标题提供的选项是可用的。有关每个头文件的信息,请查阅SOAP API开发人员指南中的SOAP Headers主题。

但是,我们不想删除的一个头是SessionHeader。它将包含我们从login()响应中获得的会话ID。继续并删除其他标题,从<urn:EmailHeader>到<urn:AssignmentRuleHeader>。你的消息看起来像这样。

Sample create() request in SoapUI

获取复制到文本文件的会话ID并将其粘贴到<urn:sessionId>标记中,替换?

我们对邮件正文做了一些更多的调整。首先,我们指定我们正在创建一个帐户。将<urn:sObjects>标记中的文本更改为如下所示:<urn:sObjects xsi:type =“urn1:Account”xmlns:xsi =“http://www.w3.org/2001/XMLSchema-instance” >。此调整使用XML实例架构声明指定正确的记录类型。

我们也想给这个帐户一个名字。在sObjects元素中添加<Name>示例SOAP Account </ Name>。也删除fieldsToNull和Id元素。现在你的消息看起来像这样。

Create() request with extraneous elements removed

在我们提出请求之前最后要做的事情。更改端点以指定您的组织的实例而不是登录,并从URI的末尾删除软件包版本。端点URI如下所示:https://na30.salesforce.com/services/Soap/c/36.0.

我们准备发送请求。再次点击绿色的三角形。有效!我们来看看回应。

SOAP response for creating an account

注意LimitInfoHeader。 此标题返回有关您的API使用情况的信息。 在上面的例子中,我们今天在15,000个允许的呼叫中取得了9个。

在响应正文中,注意<result>元素。 <success> true </ success>表示记录已成功创建。 <id>包含记录的ID,您可以在以后的请求中使用该记录。

我们介绍了使用SOAP API进行请求的基础知识。 当然,每个操作都有自己的参数和特点。 确保在开始使用SOAP API编写集成时,将SOAP API开发人员指南用作映射。

Salesforce API(2)REST

学习目标

完成本单元后,您将能够:

  • 登录到Workbench并导航到REST Explorer。
  • 使用描述资源。
  • 使用REST API创建一个帐户。
  • 使用REST API执行查询。

REST资源和方法

登陆!队长之前,我们已经看到了REST岛。在我们停靠并开始使用API​​之前,让我们来谈谈REST资源和方法。
REST资源是一条信息或某个操作(如单个数据记录,一组记录或一个查询)的抽象。 REST API中的每个资源都由一个已命名的统一资源标识符(URI)标识,并使用标准的HTTP方法(HEAD,GET,POST,PATCH,DELETE)进行访问。 REST API基于资源的使用情况,它们的URI以及它们之间的链接。

您使用资源与您的Salesforce组织进行交互。例如,您可以:

  • 检索有关可用的API版本的摘要信息。
  • 获取有关Salesforce对象的详细信息,例如帐户,用户或自定义对象。
  • 执行查询或搜索。
  • 更新或删除记录。

REST请求包含四个组件:资源URI,HTTP方法,请求标头和请求主体。请求标头指定请求的元数据。请求主体在需要时指定请求的数据。如果没有要指定的数据,则请求中将省略正文。

描述客户对象

现在是时候弄湿我们的脚了。我们将使用Workbench进行一些API调用。 Workbench是一套通过API与您的Salesforce组织进行交互的工具。因为您可以从任何HTTP发件人发出REST请求,所以还有其他许多工具可供您使用(例如,检出cURL或Postman)。但是,由于Workbench为Salesforce API提供了一个友好的框架,因此在您准备开始全面集成之前,这是一个完美的方式。

第一步是登录Workbench。

  1. 登录到您的Trailhead DE组织并导航到Workbench
  2. 对于环境,请选择 Production.
  3. 对于API版本,请选择可用的最高编号。
  4. 确保您选择 I agree to the terms of service.
  5. 点击 Login with Salesforce.

您已经到达Workbench主页。对于这个模块,我们只使用Workbench的许多工具之一,即REST资源管理器。

在顶部菜单中,选择 utilities | REST Explorer.

Workbench's REST Explorer

您可以从REST资源管理器调用REST API调用,就像从任何其他HTTP接口调用一样。文本框中的文本表示资源URI。为了方便起见,显示的URI中省略了顶级域名。例如,预填充到URI文本框中的资源的完整URI是https://foo.my.salesforce.com/services/data/v36.0。

URI上方的单选按钮表示标准的HTTP方法。要进行API调用,请输入资源URI,选择适当的方法,根据需要添加标题,然后单击 Execute.

我们来试试SObject Describe资源。此资源与GET方法结合使用时,将返回有关对象及其字段的元数据。

我们将尝试描述Account对象。将URI文本框中的现有文本替换为/services/data/vXX.0/sobjects/account/describe,其中XX映射到您正在使用的API版本。

URI for describing an account

让我们花一分钟来分解这个资源的URI。

  • /services/data—指定我们正在发出一个REST API请求
  • /v36.0—API版本号
  • /sobjects—指定我们正在访问sObject分组下的资源
  • /account—sObject被执行;在这种情况下,客户
  • /describe—Action; 在这种情况下,描述请求

现在确保选择GET方法,然后单击 Execute.

Response after we describe the account object

好的工作,队长。客户元数据出现在屏幕上。 Workbench已经很好地格式化了响应。要查看原始JSON响应,请单击 Show Raw Response.

JSON response after we describe the account object

帐户元数据以JSON形式显示在某些HTTP响应标头下方。因为REST API同时支持JSON和XML,所以让我们更改请求头以指定XML响应。在HTTP方法旁边,单击标题。对于Accept头值,将application / json替换为application / xml。您的请求标题看起来像这样。

Request header specifying XML

点击 Execute。原始的XML响应被返回。欢呼!

创建一个客户

现在让我们使用SObject资源和POST方法创建一个帐户。在URI文本框中,将现有文本替换为/services/data/vXX.0/sobjects/account,其中XX映射到您正在使用的API版本。选择POST。请注意,出现了请求正文文本区域,这是我们为新帐户指定字段值的位置。首先,让我们把Accept头改回JSON。

点击 Headers. 将Accept:application / xml改为Accept:application / json。你的请求看起来像这样。

REST request headers

在请求正文中,输入以下文本。

{
  "Name" : "NewAccount1",
  "ShippingCity" : "San Francisco"
}
点击 Execute. 你会看到如下的回应。
Successful account creation response in REST API

如果 success: true,则使用返回的ID创建帐户。展开错误文件夹以检查错误。

只是开球,让我们创建第二个帐户,而不指定帐户名称。将请求正文中的文本替换为以下文本。

{
  "ShippingCity" : "San Francisco"
}
点击 Execute.

哦,哦。您是否收到REQUIRED_FIELD_MISSING个响应?展开REQUIRED_FIELD_MISSING文件夹,然后展开字段文件夹。您的扩展响应如下所示。

We get an error when we don't provide an account name

由于名称是创建帐户的必填字段,因此服务器不处理请求,并且收到错误。谢天谢地,我们需要修复请求的所有信息都在错误响应中。让我们指定名称NewAccount2,然后在请求正文中更改运输城市。将请求正文中的文本替换为以下文本。

{
  "Name" : "NewAccount2",
  "ShippingCity" : "New York"
}

点击 Execute. 成功!

执行查询

现在让我们想象一下,您或其他用户创建了数百个帐户。您想要查找航运城市是旧金山的所有帐户的名称。您可以使用查询资源执行SOQL查询,并根据需要输入确切的记录,就像定制的宝藏地图一样。/services/data/vXX.0/query/?q=SELECT+Name+From+Account+WHERE+ShippingCity=’San+Francisco’, 其中XX映射到:用以下文字替换URI文本框中的文本您正在使用的API版本。

我们用查询字符串中的+字符替换空格,以正确编码URI。您可以从参考资料部分的链接阅读HTML URL编码。确保选择了GET方法,然后单击 Execute.

展开记录文件夹。您是否看到一个名为我们创建的第一个帐户NewAccount1的文件夹?大。点击它。现在点击属性文件夹。 url旁边是返回的帐户的资源URI。你的回应看起来像这样。

Query response returns one account record

当您编写集成时,您可以从响应中获取该URI以访问有关该帐户的更多详细信息。

Node.js和Ruby示例

现在,您对REST API的可能性有了一个甜头。当然,当您从Workbench转到编写代码时,您将使用您选择的编程语言与REST API进行交互。幸运的是,专业的Salesforce开发人员已经为几种语言编写了包装器,简化了使用REST API的过程。以下是分别使用包装器Nforce和Restforce在Node.js和Ruby中编写的两个示例查询。

使用Nforce的Node.js示例

var nforce = require('nforce');

// 创建与Salesforce连接的应用程序的连接
var org = nforce.createConnection({
  clientId: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
  redirectUri: process.env.CALLBACK_URL,
  mode: 'single'
});

// 认证并返回OAuth令牌
org.authenticate({
  username: process.env.USERNAME,
  password: process.env.PASSWORD+process.env.SECURITY_TOKEN
}, function(err, resp){
  if (!err) {
    console.log('Successfully logged in! Cached Token: ' + org.oauth.access_token);
    // 执行查询
    org.query({ query: 'select id, name from account limit 5' }, function(err, resp){
      if(!err && resp.records) {
        // 输出帐户名称
        for (i=0; i<resp.records.length;i++) {
          console.log(resp.records[i].get('name'));
        }
      }
    });
  }
  if (err) console.log(err);
});

使用Restforce的Ruby示例

require 'restforce'

// 创建与Salesforce连接的应用程序的连接
client = Restforce.new :username => ENV['USERNAME'],
  :password       => ENV['PASSWORD'],
  :security_token => ENV['SECURITY_TOKEN'],
  :client_id      => ENV['CLIENT_ID'],
  :client_secret  => ENV['CLIENT_SECRET']

// 执行查询
accounts = client.query("select id, name from account limit 5")

// 输出帐户名称
accounts.each do |account|
  p account.Name
end

Salesforce API(1)

学习目标

完成本单元后,您将能够:

  • 描述以API为先的开发方法的好处。
  • REST API,SOAP API,Bulk API和Streaming API的状态用例。
  • 命名这两种类型的API限制并描述它们的计算方式。

API首先在Salesforce

嗨,妈呀!您是否准备好在公海上航行,寻找完美的Salesforce API来进行集成?那么,船长,抓住你的眼罩,找到一只鹦鹉。您即将浏览Force.com区域并了解我们的所有API。
Salesforce API环境和海蓝一样广阔。这是因为Salesforce采用API优先方法来构建Salesforce平台上的功能。 API首先意味着在专注于设计UI之前,为功能构建强大的API。这种方法使Salesforce开发人员能够灵活地根据需要操作数据。

Salesforce知道其客户和合作伙伴总是在想办法扩展Salesforce功能和令人兴奋的AppExchange应用程序。提供一个用于在平台上开发的综合工具箱是重中之重。这种方法还可以让Salesforce在API之上构建UI,确保它们之间的行为相同。

把这个模块想象成你的API第一个伴侣。我们将一起浏览一些常规的API信息,对Salesforce的API套件进行调查,并深入使用一些常见的API。所有这些信息将为您提供所需的知识,以便为您的项目选择正确的API。

Salesforce数据API

在Salesforce API的海洋中,有一个我们在本模块中关注的常用API的关键群组。它们是REST API,SOAP API,Bulk API和Streaming API。他们一起组成了Salesforce数据API。他们的目的是让您操纵您的Salesforce数据,而其他API让您可以自定义页面布局或构建自定义开发工具。您也可以使用其他Salesforce API来操纵Salesforce数据的子集。例如,Analytics(分析)REST API侧重于分析。但是这四个API广泛应用于Salesforce核心数据的各个区域。

Salesforce data APIs

REST API

REST API是基于RESTful原则的简单而强大的Web服务。它通过REST资源和HTTP方法公开各种Salesforce功能。例如,您可以创建,读取,更新和删除(CRUD)记录,搜索或查询数据,检索对象元数据以及访问组织中有关限制的信息。 REST API支持XML和JSON。

由于REST API具有轻量级的请求和响应框架,并且易于使用,所以对于编写移动应用程序和Web应用程序非常有用。

SOAP API

SOAP API是基于具有相同名称的行业标准协议的功能强大且功能强大的Web服务。它使用Web服务描述语言(WSDL)文件来严格定义通过API访问数据的参数。 SOAP API仅支持XML。大部分SOAP API功能也可以通过REST API获得。这取决于哪个标准更好地满足您的需求。

因为SOAP API使用WSDL文件作为API和使用者之间的正式契约,所以编写服务器到服务器的集成是非常好的。

Bulk API

批量API是一种专门用于一次加载和查询大量数据的RESTful API。我们的意思是5万条记录或更多。批量API是异步的,这意味着您可以提交请求并稍后返回结果。处理大量数据时,这种方法是首选方法。 Bulk API有两个版本(1.0和2.0)。两个版本都处理大量的数据,但是我们在这个模块中使用Bulk API 2.0,因为它使用起来更容易一些。

批量API非常适合执行涉及大量记录的任务,例如首次将数据加载到组织中。

Streaming API

Streaming API是一个专门的API,用于设置在对数据进行更改时触发的通知。它使用发布 – 订阅(pub-subscribe)或发布/订阅(pub / sub)模型,用户可以在其中订阅广播某些类型的数据更改的频道。

发布/订阅模型通过消除轮询的需要来减少API请求的数量。流式API对编写应用程序非常有用,否则这些应用程序需要经常轮询更改。

API访问和身份验证

您不需要宝藏地图来访问Salesforce API。所有您需要的是以下版本之一的组织:企业版,无限制版,开发人员版,性能版或专业版(带附加组件)。确保您具有“API启用”权限,并且您已准备好开始集成。
除了SOAP API login()调用之外,所有API调用都需要身份验证。您可以使用其中一个受支持的OAuth流,也可以使用从SOAP API login()调用中检索的会话标识进行身份验证。查看开发者指南,了解您所选择的API。

API限制

任何值得他或她的盐的船长都知道何时为了船的利益而限制船员。如果船长让水手整天喝酒,什么事都做不了。同样,Salesforce也会限制每个组织的API调用次数,以确保实例的健康状况。这些限制是为了防止流氓脚本将我们的服务器砸成浮木。他们不妨碍你的日常工作。尽管如此,熟悉它们是个好主意。
有两种类型的API限制。并发限制限制了同时运行的长时间呼叫(20秒或更长)的数量。总限制限制了在24小时内滚动的呼叫数量。

并发限制因组织类型而异。对于开发者版本组织,限制是一次五个长时间运行的调用。对于沙箱组织来说,这是25个长时间运行的呼叫。

根据您购买的组织版本,许可证类型和扩展包,总数限制会有所不同。例如,Enterprise Edition组织每个Salesforce许可证可获得1000个呼叫,每个Force.com Light App许可证可获得200个呼叫。使用无限的应用包,相同的企业版组织可以获得额外的4000个电话。根据组织版本,总限制也受到最低限额和最高限额的限制,但我们不会在这里进行讨论。如果您想了解更多信息,请参阅参考资料部分中的API请求限制链接。

你有几种方法来检查你的剩余的API调用。您可以在“系统概述”页面的“API使用情况”框中查看它们。从“设置”中,在快速查找方框中输入系统概览,然后选择系统概览。您也可以在您的组织超出您指定的多个API调用时设置通知。为此,从安装程序中,在快速查找框中输入API使用通知,然后选择API使用通知。

使用REST或SOAP API时,LimitInfoHeader响应标题可以为您提供有关剩余呼叫的信息。您还可以访问REST API限制资源,了解组织中各种限制的信息。

我使用哪个API?

为您的整合需求选择正确的API是一个重要的决定。以下是我们最常用的API的一些信息,包括支持的协议,数据格式,通信范例和用例。将本节作为参考,您可以在考虑使用哪个API时返回。

请注意我们已经谈到的四个数据API。接下来我们将深入其中。

API名称 协议 数据格式 通讯
REST API REST JSON, XML 同步
SOAP API SOAP (WSDL) XML 同步
Chatter REST API REST JSON, XML 同步(照片异步处理)
Analytics REST API REST JSON, XML 同步
Bulk API REST CSV, JSON, XML 同步
Metadata API SOAP (WSDL) XML 同步
Streaming API Bayeux JSON 异步(数据流)
Apex REST API REST JSON, XML, Custom 同步
Apex SOAP API SOAP (WSDL) XML 同步
Tooling API REST or SOAP (WSDL) JSON, XML, Custom 同步

何时使用REST API

REST API提供了一个强大,方便,简单的基于REST的Web服务接口,用于与Salesforce进行交互。它的优点包括易于集成和开发,并且是移动应用程序和Web项目使用的最佳技术选择。但是,如果您有多个要处理的记录,请考虑使用Bulk API,该API基于REST原则并针对大量数据集进行了优化。

何时使用SOAP API

SOAP API为与Salesforce进行交互提供了一个强大,方便且简单的基于SOAP的Web服务界面。您可以使用SOAP API创建,检索,更新或删除记录。您还可以使用SOAP API执行搜索等等。使用任何支持Web服务的语言使用SOAP API。

例如,您可以使用SOAP API将Salesforce与您的组织的ERP和财务系统集成。您还可以向公司门户网站提供实时销售和支持信息,并使用客户信息填充关键业务系统。

何时使用Chatter REST API

使用Chatter REST API来显示Salesforce数据,尤其是在移动应用程序中。除Chatter提要,用户,组和追随者外,Chatter REST API还提供对文件,推荐,主题,通知,Data.com采购等的编程式访问。 Chatter REST API类似于其他公司提供的API,例如Facebook和Twitter,但也暴露了Chatter之外的Salesforce功能。

何时使用Analytics REST API

您可以使用Google Analytics REST API以编程方式访问Google Analytics资产(如数据集,镜头和仪表板)。将查询直接发送到Analytics平台。访问已导入到Google Analytics平台的数据集。创建和检索镜头。访问XMD信息。检索数据集版本的列表。创建和检索分析应用程序。创建,更新和检索Google Analytics仪表板。检索应用程序的依赖关系列表。确定哪些功能可供用户使用。使用快照。操作复制的数据集。

何时使用批量API

批量API基于REST原则,并针对加载或删除大量数据进行了优化。您可以使用它通过提交批来异步查询,查询所有,插入,更新,插入或删除许多记录。 Salesforce在后台处理批次。

相比之下,SOAP API针对实时客户端应用程序进行了优化,每次更新一些记录。您可以使用SOAP API来处理许多记录,但是当数据集包含数十万条记录时,SOAP API不太实用。批量API旨在使处理从数千条到数百万条记录的数据变得简单。

使用Bulk API最简单的方法是使用CSV文件在Data Loader中处理记录。使用Data Loader可以避免编写自己的客户端应用程序。

何时使用元数据API

使用元数据API检索,部署,创建,更新或删除您的组织的自定义项。最常见的用法是将更改从沙箱或测试组织迁移到生产环境。元数据API旨在用于管理自定义和用于构建可以管理元数据模型的工具,而不是数据本身。

访问Metadata API中功能的最简单方法是使用Force.com IDE或Force.com迁移工具。这两个工具都是基于元数据API构建的,分别使用标准的Eclipse和Ant工具来简化对Metadata API的处理。

  • Force.com IDE构建在Eclipse平台上,供熟悉集成开发环境的程序员使用。在IDE中编译,编译,测试和部署。
  • 如果您使用脚本或命令行在本地目录和Salesforce组织之间移动元数据,Force.com迁移工具是理想选择。

何时使用流式API

使用Streaming API接收与您定义的SOQL查询匹配的数据更改的通知。

如果要将通知从服务器推送到客户端,则Streaming API非常有用。考虑经常轮询的应用程序的流API。持续轮询Salesforce基础设施的应用程序会消耗不必要的API调用和处理时间。 Streaming API减少了不返回数据的请求的数量,对于需要数据更改的一般通知的应用程序也非常理想。

流式API使您可以减少API调用的数量并提高性能。

何时使用Apex REST API

如果要公开Apex类和方法,请使用Apex REST API,以便外部应用程序可以通过REST体系结构访问您的代码。 Apex REST API支持OAuth 2.0和会话ID进行授权。

何时使用Apex SOAP API

如果要将Apex方法公开为SOAP Web服务API,请使用Apex SOAP API,以便外部应用程序可以通过SOAP访问您的代码。

Apex SOAP API支持OAuth 2.0和会话ID进行授权。

何时使用工具API

使用工具API将Salesforce元数据与其他系统集成。元数据类型公开为sObjects,因此您可以访问复杂类型的一个组件。这个字段级访问加快了对复杂元数据类型的操作。您还可以为Force.com应用程序构建自定义开发工具。例如,使用工具API来管理和部署Apex类和触发器以及Visualforce页面和组件的工作副本。您还可以设置检查点或堆转储标记,执行匿名Apex,并访问日志记录和代码覆盖率信息。

REST和SOAP都支持。

Apex REST外部调用

学习目标

完成后,您将能够:

  • 执行外呼以接收来自外部服务的数据。
  • 执行外呼将数据发送到外部服务。
  • 使用模拟标注测试外呼。

HTTP和外呼基础

REST外呼基于HTTP。要了解外呼如何工作,了解一些有关HTTP的内容很有帮助。每个外呼请求都与HTTP方法和端点相关联。 HTTP方法指示需要什么类型的动作。
Apex callouts to an external service

最简单的请求是一个GET请求(GET是一个HTTP方法)。 GET请求意味着发送者想要从服务器获取关于资源的信息。当服务器接收并处理此请求时,它将请求信息返回给收件人。 GET请求与导航到浏览器中的地址相似。当您访问网页时,浏览器会在幕后执行GET请求。在浏览器中,导航的结果是显示的新HTML页面。用外呼,结果是响应对象。

为了说明GET请求的工作方式,打开浏览器并导航到以下URI: https://th-apex-http-callout.herokuapp.com/animals.您的浏览器以奇怪的格式显示动物列表,因为服务以JSON格式返回响应。有时GET响应也是用XML格式化的。

以下是常用HTTP方法的说明。

表1.一些常用的HTTP方法

HTTP方法 描述
GET 检索由URL标识的数据
POST 创建资源或将数据发布到服务器。
DELETE 删除由URL标识的资源。
PUT 创建或替换请求正文中发送的资源。

如果您有空闲时间,请在参考资料部分浏览所有HTTP方法的详尽列表。

除了HTTP方法之外,每个请求都会设置一个URI,这是服务所在的端点地址。例如,一个端点可以是 http://www.example.com/api/resource.在 “HTTP and Callout Basics” 单元中的示例中,端点是https://th-apex-http-callout.herokuapp.com/animals.

当服务器处理请求时,它在响应中发送一个状态码。状态码指示请求是否成功处理或是否遇到错误。如果请求成功,服务器会发送一个200的状态码。您可能已经看到一些其他的状态码,例如404没有找到文件,500没有找到内部服务器错误。

如果在浏览HTTP方法列表后仍然有空闲时间,请查看参考资料部分的所有响应状态代码列表。如果你晚上睡得很困难,这两个资源可以帮助你。

从服务获取数据

现在是时候把你的新HTTP知识用于一些Apex标注。本示例向Web服务发送GET请求以获取林地生物列表。该服务以JSON格式发送响应。 JSON本质上是一个字符串,所以内置的JSONParser类将它转换为一个对象。然后,我们可以使用该对象将每个动物的名称写入调试日志。
在本机运行示例之前,您需要使用“授权端点地址”部分中的步骤来授权标注的端点URL , https://th-apex-http-callout.herokuapp.com, 使用“授权端点地址”部分的步骤。
  1. 从设置档(打开设备齿轮图标)打开开发者控制台。 (Setup gear icon).
  2. 在开发者控制台中,选择 Debug | Open Execute Anonymous Window.
  3. 删除现有的代码并插入下面的代码片段
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint(‘https://th-apex-http-callout.herokuapp.com/animals’);
    request.setMethod(‘GET’);
    HttpResponse response = http.send(request);
    // 如果请求成功,则解析JSON响应。
    if (response.getStatusCode() == 200) {
    // 将JSON字符串反序列化为原始数据类型的集合。
    Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
    // “动物”键中的值作为列表进行投射
    List<Object> animals = (List<Object>) results.get(‘animals’);
    System.debug(‘Received the following animals:’);
    for (Object animal: animals) {
    System.debug(animal);
    }
    }
  4. 选择 Open Log, 然后单击Execute.
  5. 调试日志打开后,选择 Debug Only 查看System.debug语句的输出。
    显示动物的名字。

我们例子中的JSON相当简单并且易于解析。对于更复杂的JSON结构,可以使用JSON2Apex。该工具生成用于解析JSON结构的强类型的Apex代码。您只需粘贴JSON,该工具就会为您生成必要的Apex代码。荣耀!

发送数据到服务

HTTP 外呼的另一个常见用例是将数据发送到服务。例如,当您购买最新的贾斯汀·比伯(Justin Bieber)专辑或评论您最喜欢的“Cat in a Shark Costume Chases a Duck While Riding a Roomba”视频时,您的浏览器正在提交POST请求以提交数据。我们来看看我们如何在Apex中发送数据。
本示例向Web服务发送POST请求以添加动物名称。新名称作为JSON字符串添加到请求正文中。请求Content-Type头设置为让服务知道发送的数据是JSON格式,以便它可以适当地处理数据。该服务通过发送状态代码和所有动物列表(包括您添加的动物)作出响应。如果请求已成功处理,则状态码将返回201,因为已创建资源。如果返回201以外的任何内容,则将响应发送到调试日志。
  1. 从设置档(打开设备齿轮图标)打开开发者控制台 (Setup gear icon).
  2. 在开发者控制台中,选择 Debug | Open Execute Anonymous Window.
  3. 删除任何现有的代码并插入以下片段。
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint(‘https://th-apex-http-callout.herokuapp.com/animals’);
    request.setMethod(‘POST’);
    request.setHeader(‘Content-Type’, ‘application/json;charset=UTF-8’);
    // 将主体设置为JSON对象
    request.setBody(‘{“name”:”mighty moose”}’);
    HttpResponse response = http.send(request);
    // 解析JSON响应
    if (response.getStatusCode() != 201) {
    System.debug(‘返回的状态代码不是预期的: ‘ +
    response.getStatusCode() + ‘ ‘ + response.getStatus());
    } else {
    System.debug(response.getBody());
    }

  4. 选择打开 Open Log, 然后单击 Execute.
  5. 调试日志打开时,请选择 Debug Only 查看System.debug语句的输出。动物列表中的最后一项是 “mighty moose”.

测试外呼

外呼测试有好消息和坏消息。坏消息是Apex测试方法不支持外呼,执行标注的测试失败。好消息是测试运行时允许你“mock”外呼。模拟标注允许您指定在测试中返回的响应,而不是实际调用Web服务。你实际上在告诉运行时,“我知道这个web服务将会返回什么,所以不要在测试过程中调用它,而是返回这个数据。”在你的测试中使用模拟标注有助于确保你获得足够的代码覆盖率,代码由于标注而被跳过。

先决条件

在编写测试之前,让我们创建一个类,其中包含我们在“将数据发送到服务”单元中匿名执行的GET和POST请求示例。这些示例稍有修改,以便请求在方法和返回值中,但它们与前面的示例基本相同。

  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于课程名称,请输入AnimalsCallouts 然后单击OK.
  3. 将自动生成的代码替换为以下类定义。

    public class AnimalsCallouts {

    public static HttpResponse makeGetCallout() {
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint(‘https://th-apex-http-callout.herokuapp.com/animals’);
    request.setMethod(‘GET’);
    HttpResponse response = http.send(request);
    //如果请求成功,则解析JSON响应。
    if (response.getStatusCode() == 200) {
    // 将JSON字符串反序列化为原始数据类型的集合。
    Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
    // 将“animals”键中的值作为列表进行投射
    List<Object> animals = (List<Object>) results.get(‘animals’);
    System.debug(‘Received the following animals:’);
    for (Object animal: animals) {
    System.debug(animal);
    }
    }
    return response;
    }

    public static HttpResponse makePostCallout() {
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint(‘https://th-apex-http-callout.herokuapp.com/animals’);
    request.setMethod(‘POST’);
    request.setHeader(‘Content-Type’, ‘application/json;charset=UTF-8’);
    request.setBody(‘{“name”:”mighty moose”}’);
    HttpResponse response = http.send(request);
    // 解析JSON响应
    if (response.getStatusCode() != 201) {
    System.debug(‘预期不会返回状态码: ‘ +
    response.getStatusCode() + ‘ ‘ + response.getStatus());
    } else {
    System.debug(response.getBody());
    }
    return response;
    }

    }

  4. 按下 CTRL+S 保存..

使用StaticResourceCalloutMock测试外呼

要测试您的标注,请通过实现接口或使用静态资源来使用模拟标注。在这个例子中,我们稍后使用静态资源和模拟接口。静态资源包含要返回的响应主体。同样,使用模拟标注时,请求不会发送到端点。相反,Apex运行时知道查找在静态资源中指定的响应,并返回它。 Test.setMock方法通知运行时在测试方法中使用模拟标注。让我们看看模拟标注在行动。首先,我们创建一个包含JSON格式字符串的静态资源,用于GET请求。

  1. 在开发者控制台中,选择 File | New | Static Resource.
  2. 对于名称,输入GetAnimalResource.
  3. 对于MIME类型,选择text/plain, 即使使用JSON.
  4. 点击Submit.
  5. 在为资源打开的选项卡中,插入以下内容。确保它全部在一行上,不会中断到下一行。这个内容就是模拟标注返回的内容。这是三个林地生物阵列。
    {“animals”: [“pesky porcupine”, “hungry hippo”, “squeaky squirrel”]}
  6. 点击CTRL+S保存.

您已成功创建您的静态资源!现在,让我们为使用此资源的标注添加一个测试。

  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于类名称,输入 AnimalsCalloutsTest 然后单击OK.
  3. 将自动生成的代码替换为以下测试类定义。

    @isTest
    private class AnimalsCalloutsTest {

    @isTest static void testGetCallout() {
    // 基于静态资源创建模拟响应
    StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
    mock.setStaticResource(‘GetAnimalResource’);
    mock.setStatusCode(200);
    mock.setHeader(‘Content-Type’, ‘application/json;charset=UTF-8’);
    // 外呼与模拟响应相关联
    Test.setMock(HttpCalloutMock.class, mock);
    // 调用方法来测试
    HttpResponse result = AnimalsCallouts.makeGetCallout();
    // 验证模拟响应不为空
    System.assertNotEquals(null,result,
    ‘标注返回了一个空响应。’);
    // 验证状态码
    System.assertEquals(200,result.getStatusCode(),
    ‘状态码不是200.’);
    // 验证内容类型
    System.assertEquals(‘application/json;charset=UTF-8’,
    result.getHeader(‘Content-Type’),
    ‘The content type value is not expected.’);
    // 验证数组包含3个项目
    Map<String, Object> results = (Map<String, Object>)
    JSON.deserializeUntyped(result.getBody());
    List<Object> animals = (List<Object>) results.get(‘animals’);
    System.assertEquals(3, animals.size(),
    ‘数组只应该包含3个项目’);
    }

    }

  4. 点击CTRL+S 保存.
  5. 选择 Test | Always Run Asynchronously. 如果不选择“始终运行异步”,则测试运行只包含一个同步运行的类。您只能从“测试”选项卡打开日志,以进行同步测试运行。
    要运行测试,请选择Test | New Run.
  6. 从Test Classes列表中选择 AnimalsCalloutsTest.
  7. 点击Add Selected | Run.

测试结果显示在测试运行ID下的测试选项卡中。测试执行完成后,展开测试运行以查看详细信息。现在在“代码总体覆盖率”窗格中双击AnimalCallouts,查看测试覆盖了哪些行。

使用HttpCalloutMock测试外呼

为了测试你的POST标注,我们提供了一个HttpCalloutMock接口的实现。此接口使您能够指定在响应方法中发送的响应。您的测试类指示Apex运行时通过再次调用Test.setMock发送此假响应。对于第一个参数,传递HttpCalloutMock.class。对于第二个参数,传递一个AnimalsHttpCalloutMock的新实例,它是您的HttpCalloutMock的接口实现。 (在这个例子之后的例子中,我们将写AnimalsHttpCalloutMock)

Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());

现在添加实现HttpCalloutMock接口的类来拦截标注。如果在测试上下文中调用HTTP标注,则不会进行标注。相反,您会收到您在AnimalsHttpCalloutMock中的响应方法实现中指定的模拟响应。

  1. 在开发者控制台中,选择 File | New | Apex Class.
  2. 对于类名,输入 AnimalsHttpCalloutMock然后单击OK.
  3. 自动生成的代码替换为以下类定义。
    @isTest
    global class AnimalsHttpCalloutMock implements HttpCalloutMock {
    // 实现这个接口方法
    global HTTPResponse respond(HTTPRequest request) {
    // 创建一个假的回应
    HttpResponse response = new HttpResponse();
    response.setHeader(‘Content-Type’, ‘application/json’);
    response.setBody(‘{“animals”: [“majestic badger”, “fluffy bunny”, “scary bear”, “chicken”, “mighty moose”]}’);
    response.setStatusCode(200);
    return response;
    }
    }
  4. 点击CTRL+S保存.

在您的测试类中,创建testPostCallout方法来设置模拟标注,如下例所示。 testPostCallout方法调用Test.setMock,然后调用AnimalsCallouts类中的makePostCallout方法。然后验证返回的响应是您在模拟实现的响应方法中指定的。

  1. 修改测试类AnimalsCalloutsTest添加第二个测试方法。单击类选项卡,然后在右括号之前添加以下方法。
    @isTest static void testPostCallout() {
    // 设置模拟外呼类
    Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());
    //这会导致一个假的响应被发送
    // 从实现HttpCalloutMock的类中
    HttpResponse response = AnimalsCallouts.makePostCallout();
    //验证收到的响应是否包含假值
    String contentType = response.getHeader(‘Content-Type’);
    System.assert(contentType == ‘application/json’);
    String actualValue = response.getBody();
    System.debug(response.getBody());
    String expectedValue = ‘{“animals”: [“majestic badger”, “fluffy bunny”, “scary bear”, “chicken”, “mighty moose”]}’;
    System.assertEquals(actualValue, expectedValue);
    System.assertEquals(200, response.getStatusCode());
    }
  2. 点击CTRL+S 保存.
  3. 选择 Test | New Run.
  4. 从Test Classes列表中选择AnimalsCalloutsTest.
  5. 点击 Add Selected | Run.

    测试结果显示在“测试”选项卡下的新测试运行ID下。当测试执行完成时,展开测试运行以查看有关这两个测试的详细信息。

更多…

了解如何在触发器和异步Apex中使用标注,以及如何制作异步标注。
当从一个方法进行标注时,该方法在执行后续代码行之前,等待外部服务发回标注响应。或者,您可以将标注代码置于用@future(callout = true)注释的异步方法中,或者使用Queueable Apex。这样,标注在单独的线程上运行,调用方法的执行不会被阻止。

从触发器进行标注时,标注不得在等待响应时阻止触发过程。为了使触发器能够进行标注,包含标注代码的方法必须使用@future(callout = true)进行注释,以便在单独的线程中运行。

自己动手尝试

创建一个调用REST端点的Apex类并编写一个测试类。

为了通过这个练习,创建一个调用REST端点的Apex类来返回一个动物的名字,编写单元测试,使用模拟响应实现类的100%代码覆盖率,并运行Apex测试。

  • Apex类必须被称为“AnimalLocator”,有一个“getAnimalNameById”方法接受一个Integer并返回一个String。
  • ‘getAnimalNameById’方法必须使用传入方法的ID调用https://th-apex-http-callout.herokuapp.com/animals/:id。 该方法返回“name”属性(即动物名称)的值。
  • 创建一个名为AnimalLocatorTest的测试类,该类使用名为AnimalLocatorMock的模拟类模拟标注响应。
  • 单元测试必须涵盖AnimalLocator类中包含的所有代码行,从而得到100%的代码覆盖率。
  • R在尝试验证这个挑战之前,至少运行一次测试类(通过“全部运行”测试开发者控制台)。