Salesforce与网络服务的通信
在Salesforce中可以利用Apex类与远程站点的网络服务进行通信。当远程网络服务支持REST方法时,开发者可以利用Apex代码进行数据的操作。
设置远程站点
在设置界面下,搜索“远程站点”,点击“安全性控制”菜单项下的“远程站点设置”链接,即可进入远程站点的一览表。在此处可以新建、编辑、删除远程站点。这些远程站点可以作为网络服务接口。
Apex REST请求
Apex中可以以HttpRequest类为核心和网络服务接口进行REST通信。HttpRequest类包括了“setEndpoint()”、“setMethod()”、“setHeader()”、“setBody()”等函数。
假设有一个网络服务接口“https://example.service.com/laguages”,可以对编程语言的名字进行操作。当执行GET请求时,会给出一个json的结果,包含了一个“languages”数组,里面是若干“language”对象。
GET请求
用以下代码可以实现GET请求:
public class LanguageCallouts {
public static HttpResponse makeGetCallout() {
Http http = new Http();
HttpRequest request = new HttpRequest();
// 设置网络服务接口的地址
request.setEndpoint('https://example.service.com/laguages');
// 设置REST方法
request.setMethod('GET');
// 发送HTTP请求
HttpResponse response = http.send(request);
// 检查HTTP通信结果状态代码
if (response.getStatusCode() == 200) {
// 将通信结果转化为Map类型变量
Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
// 对结果进行处理,得到language对象
List<Object> languages = (List<Object>) results.get('languages');
}
return response;
}
}
POST请求
用以下代码可以实现POST请求:
public class LanguageCallouts {
public static HttpResponse makePostCallout() {
Http http = new Http();
HttpRequest request = new HttpRequest();
// 设置网络服务接口的地址
request.setEndpoint('https://example.service.com/laguages');
// 设置REST方法
request.setMethod('POST');
// 设置请求的Header,类型为JSON
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
// 将一个JSON对象传入请求的Body,设置编程语言的名字
request.setBody('{"name":"Apex"}');
// 发送HTTP请求
HttpResponse response = http.send(request);
// 检查HTTP通信结果状态代码
if (response.getStatusCode() == 201) {
// 在控制台输出通信结果
System.debug(response.getBody());
}
return response;
}
}
对API请求进行单元测试
在Apex的单元测试中,如果被测试的函数涉及到对网络服务的请求,则单元测试函数无法真正的连接到网络服务接口进行数据传输。开发者必须建立模拟数据。
Apex中提供了“StaticResourceCalloutMock()”函数和“HttpCalloutMock”接口来对网络服务接口的请求结果进行模拟
StaticResourceCalloutMock()函数
在使用StaticResourceCalloutMock()函数前需要在系统中建立一个静态资源,其中预设了网络服务请求的结果。
比如要测试上面示例代码中的“makeGetCallout()”函数,则首先在系统中建立一个静态资源“mockRequestResult”,其中包含一个JSON格式的字符串:
{"languages": ["C", "PHP", "Java"]}
在单元测试函数中,写入如下代码:
@isTest
static void testGetCallout() {
StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
// 设置静态资源
mock.setStaticResource('mockRequestResult');
// 设置模拟网络服务请求的返回结果
mock.setStatusCode(200);
// 设置模拟网络服务请求的Header部分
mock.setHeader('Content-Type', 'application/json;charset=UTF-8');
// 设置模拟的网络服务请求
Test.setMock(HttpCalloutMock.class, mock);
// 运行要测试的函数
HttpResponse result = LanguageCallouts.makeGetCallout();
// 检查返回的结果。该结果应该与mock中设置的内容相同,也包含了静态资源里的内容
System.assertNotEquals(null, result);
System.assertEquals(200, result.getStatusCode());
System.assertEquals('application/json;charset=UTF-8', result.getHeader('Content-Type'));
Map<String, Object> results = (Map<String, Object>) JSON.deserializedUntyped(result.getBody());
List<Object> languages = (List<Object>) results.get('languages');
System.assertEquals(3, languages.size());
}
HttpCalloutMock接口
HttpCalloutMock接口的使用方法与StaticResourceCalloutMock()函数类似,不过不需要先建立静态资源,而需要预先建立一个全局Apex类,该类实现了HttpCalloutMock接口,并预设了网络服务请求的模拟数据。
比如要测试上面示例代码中的“makePostCallout()”函数,则首先在系统中建立一个“LanguagesHttpCalloutMock”类,在其中设置:
@isTest
global class LanguagesHttpCalloutMock implements HttpCalloutMOck {
global HttpResponse response(HttpRequest request) {
HttpResponse response = new HttpResponse();
response.setHeader('Content-Type', 'application/json');
response.setBody('{"languages": ["C", "PHP", "Java", "Apex"]}');
response.setStatusCode(200);
return response;
}
}
在单元测试函数中,写入如下代码:
@isTest
static void testPostCallout() {
// 设置模拟的网络服务请求
Test.setMock(HttpCalloutMock.class, new LanguagesHttpCalloutMock());
// 运行要测试的函数
HttpResponse result = LanguageCallouts.makePostCallout();
// 检查返回的结果
System.assertEquals(200, result.getStatusCode());
System.assertEquals('application/json;charset=UTF-8', result.getHeader('Content-Type'));
String expectedResult = '{"languages": ["C", "PHP", "Java", "Apex"]}';
System.assertEquals(response.getBody(), expectedResult);
}
Apex类作为网络服务
Apex类可以被扩展为网络服务,外部的请求可以通过此类来与Salesforce中的数据进行通信。
将Apex类定义为REST服务类
将Apex类定义为REST服务类只需要以下步骤:
- 将类定义为全局类
- 将特定注解添加到类和函数的定义
比如:
@RestResource(urlMapping='/Account/*)
global with sharing class ExampleRestClass {
@HttpGet
global static Account getAccount() {
// ...
}
}
代码讲解:
- 在类的定义上方,添加了@RestResource注解,并定义了“urlMapping”属性。这样,该Salesforce中的特定URL便可以作为REST服务的端点。在此示例中,外部请求通过URL “https://xxx.salesforce.com/services/apexrest/Account/” 就可以调用此类。需要注意的是,“urlMapping”属性是区分大小写的。
- 在函数的上方,添加了@HttpGet注解,说明此类相应“GET”方法。同样,基于标准的REST方法,还有其他注解:@HttpPost, @HttpDelete, @HttpPut, @HttpPatch等。
将Apex类定义为SOAP服务类
将Apex类定义为SOAP服务类和定义为REST服务类的步骤类似,只不过不需要注解,而是直接用“webservice”关键字定义函数。比如:
global with sharing class ExampleSoapClass {
webservice static Account getAccount(String Id) {
// ...
}
}
从设置界面的“Apex 类”链接进入Apex类一览表,再进入该类的详细信息页面,即可下载该类对应的WSDL文件,用于SOAP请求。