Salesforce-API

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在尝试验证这个挑战之前,至少运行一次测试类(通过“全部运行”测试开发者控制台)。

你可能也会喜欢...