Salesforce 使用Apex调用外部数据的接口有两种方式:SOAP 和 REST
- SOAP:Web服务使用XML格式的文件调用服务器,通常需要WSDL文档来生成代码
- REST:HTTP使用REST格式的文件调用服务器,推荐使用
基于WSDL的CallOut适用于SOAP方式,HTTP方式可以使用任何的HTTP Service,SOAP或者REST都可以,推荐使用REST方式. 在调用外部站点时,需要对外部站点进行授权 Setup → Remote Site Settings,
- 新建一个远程站点
- 输入远程站点名称:RESTTEST
- 输入远程站点URL:https://th-apex-http-callout.herokuapp.com
- 输入站点描述信息:Salesforce REST 接口测试链接
- 点击保存
REST方式调用外部接口
REST调用请求的传输原理如图:
HTTP常见请求包括GET和POST请求两种(还有DELETE和PUT等),调用服务器请求时,同时返回的还包括请求状态码
常见的状态码:
- 200 请求成功
- 404 请求找不到文件
- 500 内部服务器错误
POST方式获取服务器数据示例:
// 调用一个HTTP请求 HTTP http = new HTTP(); // request 发起请求 HttpRequest request = new HttpRequest(); // 传递请求地址,注意将该地址添加到远程站点列表 request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals'); // 传递请求方式POST request.setMethod('POST'); // 传递HTTP请求头参数,编码解析格式UTF-8 request.setHeader('Content-Type','application/json;charset=UTF-8'); // 传递请求参数,使用JSON串的格式 request.setBody('{"name":"mighty moose"}'); // response 响应请求 HttpResponse response = http.send(request); if(response.getStatusCode() != 201){ String str = response.getStatusCode() + ' ' + response.getStatus(); System.debug('The status code returned was not expected: ' + str); }else{ System.debug(response.getBody()); }
Tips:Callout方法在执行后续代码的时候,会等待外部服务器的响应结果,所以Callout方法同样可以在@future(callout=true) 中进行调用,这样Callout方法可以在单独的进程中调用,不会阻塞其他进程,尤其在触发器中调用Callout方法时,必须将其包含在含有@future(callout=true) 的方法中进行
GET方式获取服务器数据示例:
global class AnimalLocator { public static String getAnimalNameById(Integer id){ Http http = new Http(); HttpRequest request = new HttpRequest(); // 设置服务器参数 request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/' + id); // 设置请求方式 request.setMethod('GET'); HttpResponse response = new HttpResponse(); // 发送请求 response = http.send(request); String name = ''; if(response.getStatusCode() == 200){ // 将JSON数据格式化返回 Map<String,Object> results = (Map<String,Object>)JSON.deserializeUntyped(response.getBody()); System.debug('results:' + results); Map<String,Object> animal = (Map<String,Object>)results.get('animal'); System.debug('animal:' + animal); name = (String)animal.get('name'); } return name; } }
GET请求类似直接在浏览器中输入URL访问网页,POST请求类似用户在页面上提交的表单信息,相对来说POST方式传输数据安全性更高,GET方式更方便,可以直接在URL后面拼接参数,但是在长度上有所限制.
对于Callout测试类来说,常规的测试类在执行Callout时会报错,所以这里需要模拟接口数据来完成测试类,一般包括StaticResourceCalloutMock和HttpCalloutMock两种方式
使用StaticResourceCalloutMock来完成测试类,简单说就是将接口响应的数据放在静态资源中,然后在测试类中直接调用静态资源中的结果作为模拟服务器响应数据.
创建一个静态资源,名为GetAnimalLocator,格式为text/plain,输入模拟数据如下:
创建测试类:AnimalLocatorTest
@isTest public class AnimalLocatorTest { @isTest static void testWithStaticResourceCalloutMock(){ StaticResourceCalloutMock mock = new StaticResourceCalloutMock(); // 获取 静态资源 GetAnimalLocator的数据 mock.setStaticResource('GetAnimalLocator'); // 模拟返回码为200 mock.setStatusCode(200); // 模拟调用服务器Hearder参数 mock.setHeader('Content-Type', 'application/json;charset=UTF-8'); // 核心步骤,模拟服务器 Test.setMock(HttpCalloutMock.class, mock); // 调用接口 String name = AnimalLocator.getAnimalNameById(1); System.debug('name:' + name); } }
使用HttpCalloutMock方式来完成测试类,需要新增一个继承HttpCalloutMock的Apex类,实现respond方法
@istest global class AnimalLocatorMock implements HttpCalloutMock{ global HTTPResponse respond(HTTPRequest request) { HttpResponse response = new HttpResponse(); response.setHeader('Content-Type', 'application/json'); response.setBody('{"animal":{"id":1,"name":"test name","eats":"meats","says":"good movie"}}'); response.setStatusCode(200); return response; } }
实现了HttpCalloutMock的具体类后,测试类的实现就比较简单了
@isTest public class AnimalLocatorTest { @isTest static void testGetCalloutWithHttpCalloutMock(){ Test.setMock(HttpCalloutMock.class, new AnimalLocatorMock()); String name = AnimalLocator.getAnimalNameById(1); System.debug('name:' + name); } }
Tips:可以使用工具JSON2Apex来将json串转换成实体类
参考数据: