学习目标
- 描述两种类型的Apex Web服务并提供这些服务的高级概述。
- 创建一个包含每个HTTP方法的方法的Apex REST类。
- 使用端点调用自定义的Apex REST方法。
- 通过以JSON格式发送请求主体,将数据传递给自定义的Apex REST方法。
- 编写Apex REST方法的测试方法,并在测试REST请求中设置属性。
- 通过调用具有参数值的方法编写Apex REST方法的测试方法。
将Apex类公开为Web服务
例如,假设贵公司的呼叫中心正在使用内部应用程序来管理本地资源。预计客户支持代表将使用相同的应用程序来执行其日常工作,包括管理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() {
// 添加你的代码
}
}
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) {
// 添加你的代码
}
}
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类。
- 从设置档(打开设备齿轮图标)打开开发者控制台。
- 在开发者控制台中,选择 .
- 对于课程名称,输入CaseManager,然后单击 OK.
- 将自动生成的代码替换为以下类定义。
@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; } }
- 按下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服务。
- 导航到 https://workbench.developerforce.com/login.php.
- 对于环境,请选择 Production.
- 从“API版本”下拉列表中选择最新的API版本。
- 接受服务条款,然后单击 Login with Salesforce.
- 要允许工作台访问您的信息,请单击 Allow.
- 输入您的登录凭据,然后单击 Log in to Salesforce.
- 登录后,选择 .
- 选择 POST.
- REST Explorer接受的URL路径相对于您的组织的实例URL。仅提供追加到实例URL的路径。在相对URI输入字段中,将缺省URI替换为/services/apexrest/Cases/.
- 对于请求主体,插入要插入的对象的以下JSON字符串表示形式。
{ "subject" : "Bigfoot Sighting!", "status" : "New", "origin" : "Phone", "priority" : "Low" }
- 点击 Execute.
该调用调用与POST HTTP方法关联的方法,即createCase方法。
- 要查看返回的响应,请单击 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方法。
- 在Workbench中,选择 GET.
- 输入URI /services/apexrest/Cases/<Record ID>,将 <Record ID> 替换为您在上一步中创建的记录的ID。
-
点击 Execute.
该调用调用与GET HTTP方法关联的方法,即getCaseById方法。
-
要查看返回的响应,请单击 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'
现在输入您的cURL命令(将与以下内容类似),以调用您的Apex REST服务并返回案例信息。
curl https://yourInstance.salesforce.com/services/apexrest/Cases/<Record_ID> -H 'Authorization: Bearer <your_session_id>' -H 'X-PrettyPrint:1'
使用自定义PUT或PATCH方法更新数据
使用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;
}
- 在Workbench REST Explorer中,选择 PUT.
- 对于URI,输入 /services/apexrest/Cases/.
- upsertCase方法期望字段值在请求主体中传递。为请求主体添加以下内容,然后将<Record ID>替换为您之前创建的案例记录的ID。
{ "id": "<Record_ID>", "status" : "Working", "subject" : "Bigfoot Sighting!", "priority" : "Medium" }
- 点击 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;
}
- 在Workbench REST Explorer中,单击 PATCH.
- 对于URI,输入 /services/apexrest/Cases/<Record ID>. 将<Record ID> 替换为先前创建的案例记录的ID。在请求正文中输入以下JSON。
{ "status" : "Escalated", "priority" : "High" }
- 点击 Execute.
该请求调用REST服务中的updateCaseFields方法。案例记录的状态和优先级字段被更新为新值。要检查这些字段,请通过导航到https://yourInstance.salesforce.com/<Record ID>在Salesforce中查看此记录。
测试您的Apex 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;
现在,让我们将整个课程保存在Developer Console中,然后运行结果。
- 在开发者控制台中,选择 .
- 对于类名称,输入CaseManagerTest,然后单击 OK.
- 将自动生成的代码替换为以下类定义。
@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; } }
- 按下CTRL + S保存。
- 通过选择测试|运行您的组织中的所有 .
测试结果显示在“测试”选项卡中。测试执行完成后,请检查“总体代码覆盖率”窗格中的CaseManager行。这是在100%的覆盖面。
告诉我更多…
- 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关键字声明时会强制执行共享规则。