画布开发(4)Canvas SDK

学习目标

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

  • 引用Canvas SDK
  • 验证
  • 在您的画布应用程序中获取上下文
  • 跨域XHR
  • 调整画布应用程序的大小
  • 实施Canvas应用程序事件
  • 在Canvas应用程序中使用Streaming API
  • 在Canvas应用程序中进行调试
  • 在Canvas App中使用标签

Canvas是一组工具,使您可以在Salesforce中集成您的应用程序。 此框架包含一个SDK,您可以使用该SDK对应用程序进行身份验证并从Salesforce检索数据。 Canvas SDK和代码示例可在GitHub上获得,网址为https://github.com/forcedotcom/salesforcecanvasframeworksdk。

Canvas SDK已经过版本化,并且与每个版本中的API版本相匹配。 目前的版本是44.0。
您可以通过调用version方法找到您拥有的SDK版本。 可以在以下位置找到此开发人员指南的早期版本
https://developer.salesforce.com/page/Earlier_Reference_Documentation。

引用Canvas SDK

Canvas SDK可以在GitHub上使用,您可以通过两个选项从画布应用程序中引用它。

  • 在您自己的Web服务器上托管SDK并在那里访问它
  • 访问Salesforce服务器上托管的SDK

例如,如果您在自己的Web服务器上托管SDK,那么这就是include语句的样子:

以下是引用托管SDK时include语句的外观:

如果要在Web应用程序或Visualforce页面中包含其中一个SDK文件,则在Salesforce服务器上引用SDK的功能非常有用。

Authentication

创建画布应用程序时,可以使用以下身份验证方法之一:

  • 签名请求 – 画布应用程序的默认身份验证方法。签名请求授权流程取决于您是否配置画布应用程序,以便管理员为用户提供对画布应用程序的访问权限,或者用户是否可以自行授权。如果管理员允许用户访问画布应用程序,或者用户通过批准/拒绝OAuth流程批准了画布应用程序,则会向画布应用程序提供包含使用者密钥,访问令牌和其他上下文信息的签名请求。 。
  • OAuth 2.0-Canvas应用程序可以使用OAuth 2.0协议来验证和获取访问令牌。有关OAuth和Lightning平台的更多信息,请参阅在Salesforce中深入挖掘OAuth 2.0。

签名请求身份验证

This is the default authorization method for canvas apps. The signed request authorization flow varies depending on whether the canvas app’s Permitted Users field is set to “Admin approved users are pre-authorized” or “All users may self-authorize.”

Permitted Users ValueCanvas App Accessibility当用户需要批准画布应用程序时POST or GET Behavior
管理员批准的用户已获得预授权一旦管理员在组织中安装该应用程序并配置哪些用户可以看到该应用程序,该应用程序就可供用户访问。用户无需批准或拒绝访问。NeverSalesforce对画布应用程序执行POST,其中包含签名请求正文中包含的所有授权信息,包括刷新令牌。
所有用户都可以自我授权所有用户都可以访问该应用,但会提示用户批准或拒绝访问该应用。• 用户第一次打开应用程序
• 如果管理员撤消了访问令牌
• 如果管理员为令牌设置了时间限制并且超出了时间限制
如果用户之前已批准该应用程序并且访问权限尚未被撤销或过期,则Salesforce会使用已签名的请求有效内容对画布应用程序执行POST。
如果用户尚未批准该应用程序,或者该访问权限已被撤销或过期,则Salesforce会对画布应用程序URL执行GET。画布应用程序必须通过接受调用并查找URL参数来处理GET
_sfdc_canvas_authvalue。如果画布应用程序收到此参数值,则画布应用程序应启动批准或拒绝OAuth流程。
_sfdc_canvas_authvalue = user_approval_required
启动OAuth流并且用户批准应用程序后,画布应用程序应使用参数true调用repost()方法以检索已签名的请求。

签名的请求信息可以使用客户端密钥进行验证,并用于自定义应用程序,并随后调用Salesforce。

签名请求是以下连接的元素的字符串:

  • 使用HMAC SHA-256算法加密的画布应用程序使用者密钥
  • A period (”。”)
  • Base64中编码的上下文和授权令牌JSON

签名的请求看起来与此类似,但会更长:

9Rpl6rE7R2bSNjoSfYdERk8nffmgtKQNhr5U/5eSJPI=.eyJjb250ZXh0Ijp7InVzZXIiOnsibGFuZ3V…. 签名请求注意事项:

  • Salesforce在调用画布应用程序URL时执行HTTP POST或GET,具体取决于“允许的用户”值以及是否返回刷新令牌。
  • 需要服务器端代码来验证和解码请求。
  • 您可以在调用应用程序后,使用SDK按需请求已签名的请求。

Canvas App用户流程签名请求

此图显示了使用签名请求身份验证的画布应用程序的用户流。

验证和解码签名请求

使用签名请求时,Salesforce会将用户上下文和身份验证信息传递到画布应用程序URL。要确保签名的请求有效,您必须使用特定的画布应用程序使用者密钥验证签名的请求是否已签名。如果使用了正确的消费者秘密,那么您可以信任上下文;否则,您可以假设该请求不是由Salesforce发起的。要验证和解码已签名的请求,您的应用程序应:

1.接收包含Salesforce初始签名请求的POST消息。
2.在第一个期间拆分已签名的请求。结果是两个字符串:使用使用者机密签名的散列的Based64上下文和Base64编码的上下文本身。
3.使用HMAC SHA-256算法对Base64编码的上下文进行哈希处理,并使用您的使用者密钥对其进行签名。

  1. 接收包含Salesforce初始签名请求的POST消息。
  2. 在第一个期间拆分已签名的请求。结果是两个字符串:使用使用者机密签名的散列的Based64上下文和Base64编码的上下文本身。
  3. 使用HMAC SHA-256算法对Base64编码的上下文进行哈希处理,并使用您的使用者密钥对其进行签名。
  4. Base64编码在上一步中创建的字符串。
  5. 将Base64编码的字符串与使用您在步骤2中收到的使用者密钥签名的散列Base64上下文进行比较。

如果这两个值相同,那么您就知道已签名的请求是使用您的使用者密钥签名的,并且可以信任。从那里,您可以Base64解码编码的上下文并解析出您需要的任何值。有关这些值的更多信息,请参阅CanvasRequest。如果两个字符串不同,则请求未使用您的使用者密钥进行散列和签名,您应该将相应的消息返回给用户。

验证和解码的功能

要验证已签名的请求,您可以调用Canvas SDK中的以下函数(在
SalesforceCanvasFrameworkSDK的\ src \主\ java的\帆布\ SignedRequest.java):

  • verifyAndDecode – 将已签名请求的已验证和已解码版本作为Java对象返回。
  • verifyAndDecodeAsJson – 将已签名请求的经过验证和解码的版本作为JSON格式的字符串返回。

以下代码示例演示如何使用SDK中的函数验证和解码签名请求。此代码在此期间拆分签名的请求字符串,以解析签名的秘密和Base64 JSON字符串。然后,它会加密使用HMAC SHA-256算法签名的画布应用程序使用者密钥,并将加密值与Salesforce发送给您的加密值进行比较。

如果这两个值相同,则表示上下文有效且来自Salesforce。如果这两个值不同,则请求不是来自Salesforce。

/**
*The utility method can be used to validate/verify the signed request.
*In this case, the signed request is verified that it’s from Salesforce and that
*it has not been tampered with.
*This utility class has two methods. One verifies and decodes the request
*as a Java object, the other as a JSON String.
*/
public class SignedRequest {
public static CanvasRequest verifyAndDecode(String input, String secret)
throws SecurityException {
String[] split = getParts(input);
String encodedSig = split[0];
String encodedEnvelope = split[1];
// Deserialize the JSON body.
String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(CanvasRequest.class);
CanvasRequest canvasRequest;
String algorithm;
try {
canvasRequest = reader.readValue(json_envelope); algorithm = canvasRequest.getAlgorithm() == null ?
“HMACSHA256” : canvasRequest.getAlgorithm();
} catch (IOException e) {
throw new SecurityException(String.format(“Error [%s] deserializing JSON to Object [%s]”, e.getMessage(), CanvasRequest.class.getName()), e);
}
verify(secret, algorithm, encodedEnvelope, encodedSig);
// If we got this far, then the request was not tampered with.
// Return the request as a Java object.
return canvasRequest;
}
public static String verifyAndDecodeAsJson(String input, String secret) throws SecurityException {
String[] split = getParts(input);
String encodedSig = split[0];
String encodedEnvelope = split[1];
String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
ObjectMapper mapper = new ObjectMapper();
String algorithm; StringWriter writer;
TypeReference<HashMap<String,Object>> typeRef
= new TypeReference<HashMap<String, Object>>() { };
try {
HashMap<String,Object> o = mapper.readValue(json_envelope, typeRef);
writer = new StringWriter();
mapper.writeValue(writer, o);
algorithm = (String)o.get(“algorithm”);
} catch (IOException e) {
throw new SecurityException(String.format(“Error [%s] deserializing JSON to Object [%s]”, e.getMessage(),
typeRef.getClass().getName()), e);
}
verify(secret, algorithm, encodedEnvelope, encodedSig);
// If we got this far, then the request was not tampered with.
// Return the request as a JSON string.
return writer.toString();
}
private static String[] getParts(String input) {
if (input == null || input.indexOf(“.”) <= 0) {
throw new SecurityException(String.format(“Input [%s] doesn’t look like a signed request”, input));
}
String[] split = input.split(“[.]”, 2); return split;
}
private static void verify(String secret, String algorithm, String encodedEnvelope, String encodedSig )
throws SecurityException
{
if (secret == null || secret.trim().length() == 0) {
throw new IllegalArgumentException(“secret is null, did you set your environment variable CANVAS_CONSUMER_SECRET?”);
}
SecretKey hmacKey = null;
try {
byte[] key = secret.getBytes();
hmacKey = new SecretKeySpec(key, algorithm);
Mac mac = Mac.getInstance(algorithm); mac.init(hmacKey);
// Check to see if the body was tampered with.
byte[] digest = mac.doFinal(encodedEnvelope.getBytes());
byte[] decode_sig = new Base64(true).decode(encodedSig);
if (! Arrays.equals(digest, decode_sig)) {
String label = “Warning: Request was tampered with”; throw new SecurityException(label);
}
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(String.format(“Problem with algorithm [%s] Error [%s]”, algorithm, e.getMessage()), e);
} catch (InvalidKeyException e) {
throw new SecurityException(String.format(“Problem with key [%s] Error [%s]”, hmacKey, e.getMessage()), e);
}
// If we got here and didn’t throw a SecurityException then all is good.
}
}

调用verifyAndDecode函数

以下代码显示了获取签名请求,然后使用verifyAndDecode函数验证和解码请求的示例。

// From a JSP or servlet.
<%@ page import=”canvas.SignedRequest” %>
<%@ page import=”java.util.Map” %>
<%
// Pull the signed request out of the request body and verify/decode it. Map<String, String[]> parameters = request.getParameterMap();
String[] signedRequest = parameters.get(“signed_request”); if (signedRequest == null) {%>
This app must be invoked via a signed request!<% return;
}
String yourConsumerSecret=System.getenv(“CANVAS_CONSUMER_SECRET”); String signedRequest = SignedRequest.verifyAndDecode(signedRequest[0],
yourConsumerSecret);
%>

// From JavaScript, you can handle the signed request as needed. var signedRequest = ‘<%=signedRequestJson%>’;

调用verifyAndDecodeAsJson函数

以下代码显示了获取签名请求,使用verifyAndDecodeAsJson函数验证和解码请求以及解析返回的JSON结果的示例。

// From a JSP or servlet.
<%@ page import=”canvas.SignedRequest” %>
<%@ page import=”java.util.Map” %>
<%
// Pull the signed request out of the request body and verify/decode it. Map<String, String[]> parameters = request.getParameterMap();
String[] signedRequest = parameters.get(“signed_request”); if (signedRequest == null) {%>
This App must be invoked via a signed request!<% return;
}
String yourConsumerSecret=System.getenv(“CANVAS_CONSUMER_SECRET”);
String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret);
%>

// From JavaScript, you can parse with your favorite JSON library. var signedRequest = JSON.parse(‘<%=signedRequestJson%>’);

请求签名请求

如果您的画布应用程序设置为使用已签名的身份验证请求,则可以使用SDK按需请求已签名的请求。您的应用可以要求Salesforce重新发送加载应用时使用的已签名请求或发送新的已签名请求。在重定向应用程序后刷新过期的会话或获取身份验证信息时,请求已签名的请求非常有用。

您可以使用SDK中的refreshSignedRequest()或repost()JavaScript方法按需请求签名请求。 refreshSignedRequest()通过回调返回一个新签名的请求,而repost()要求父窗口向您的画布应用程序发起一个新的POST,并使用刷新的签名请求重新加载应用页面。

无论何时需要刷新应用程序的签名请求,请使用这些方法。以下是一些示例方案,您可能需要刷新已签名的请求信息。

  • 签名请求中的OAuth令牌通常在两小时后过期。如果OAuth令牌已过期,并且您需要发出其他API请求,则可以调用refreshSignedRequest()以获取新的OAuth令牌,而不会中断用户。
  • Canvas应用程序可能需要使用重定向,尤其是在尝试跨多个系统提供单点登录功能时。如果您的画布应用程序使用重定向,则重定向URL将不会收到包含初始签名请求信息的原始请求正文。您可以使用新方法再次请求已签名的请求。
  • 在2014年夏季及之后,画布应用程序可以是用户认可的应用程序。如果用户未批准您的用户批准的应用,则您的应用将不会获得初始签名请求POST。相反,您的应用需要通过OAuth批准,然后您可以调用repost()来获取已签名的请求。

必须将您的画布应用程序配置为使用签名的身份验证请求才能使用这些方法。 您还需要在JavaScript代码中引用canvas-all.js,如引用Canvas SDK中所述。

请求成功后,您的画布应用程序必须验证返回的SignedRequest信息。 有关验证从Salesforce接收的已签名请求信息的详细信息,请参阅验证和解码签名请求。

Using refreshSignedRequest() to Obtain a Signed Request

使用refreshSignedRequest()通过您提供的回调请求新签名的请求。 在此示例中,使用回调调用refreshSignedRequest(),该回调检查响应代码,然后将签名的请求解析为使用使用者密钥和Base64编码的上下文本身签名的散列的Based64上下文。

// Gets a signed request on demand. Sfdc.canvas.client.refreshSignedRequest(function(data) {
if (data.status === 200) {
var signedRequest = data.payload.response; var part = signedRequest.split(‘.’)[1];
var obj = JSON.parse(Sfdc.canvas.decode(part));
}
}

Using repost() to Obtain a Signed Request

使用repost()指示父窗口向画布应用程序URL发送新的POST。 POST包含加载应用程序时使用的签名请求或新签名的请求。 与refreshSignedRequest()不同,使用repost()重新加载画布应用页面。 以下示例调用repost(),询问原始签名请求。

// Gets a signed request on demand, without refreshing the signed request. Sfdc.canvas.client.repost();

The following example calls repost(), asking for a new signed request.

// Gets a signed request on demand, first by refreshing the signed request. Sfdc.canvas.client.repost({refresh : true});

OAuth身份验证

Canvas支持OAuth 2.0进行授权。使用OAuth时,您有两种选择:

  • Web服务器OAuth身份验证流程 – 当用户运行您的画布应用程序时,他们可以授权应用程序访问其数据。这要求每个用户允许画布应用访问其信息。有关详细信息,请参阅如何使用Web服务器OAuth身份验证流程对身份验证进行身份验证?在REST API开发人员指南中。
  • 用户代理OAuth身份验证流程 – 当用户运行您的画布应用程序时,他们可以通过仅使用浏览器进行身份验证来授权应用程序访问其数据。与Web Server OAuth身份验证一样,此选项要求每个用户允许画布应用访问其信息。
    建议您在开发期间而不是在生产中使用此授权方法,因为使用此类授权不需要服务器端代码,也无需将开发计算机公开到Internet。有关详细信息,请参阅“REST API开发人员指南”中的了解用户代理OAuth身份验证流程。

无论您实施哪种OAuth流,canvas应用程序都必须提供用于启动基于标准的OAuth流的代码。 OAuth注意事项包括:

  • Salesforce在调用画布应用程序URL时执行HTTP GET。
  • 使用用户代理OAuth,可以在浏览器中执行所有授权(不需要服务器端代码)。

如果您有一个使用OAuth身份验证的现有连接应用,并且您希望将该应用作为画布应用公开,那么您有两个选择。首先,您可以编辑现有应用程序(创建新版本)并向其添加画布应用程序信息。这意味着您的应用可以继续使用相同的客户端ID和消费者密钥。第二个选项是创建一个新的画布应用程序。如果您这样做,您将获得新的客户端ID和消费者保密信息,并且您需要使用该信息更新您的应用。

Canvas App用户流 – OAuth

如果您的画布应用程序使用OAuth身份验证,则用户体验会因画布应用程序在用户界面中的位置以及如何设置用户访问权限而异。此图显示了使用OAuth身份验证的画布应用程序的用户流。

启动OAuth流程

以下代码示例显示如何使用OAuth在画布应用程序中启动授权过程。

<html>
<head>
<script type=”text/javascript” src=”/sdk/js/canvas-all.js”></script>
</head>
<body>
<script>
function loginHandler(e) { var uri;
if (! Sfdc.canvas.oauth.loggedin()) { uri = Sfdc.canvas.oauth.loginUrl(); Sfdc.canvas.oauth.login(
{uri : uri,
params: {
response_type : “token”,
client_id : “3MVG9lKcPoNINVBLigmW.8dAn4L5HwY VBzxbW5FFdzvU0re2f7o9aHJNUpY9ACdh.3SUgw5rF2nSsC9_cRqzD”, redirect_uri : encodeURIComponent(
“https://demoapp1234.herokuapp.com/sdk/callback.html”)
}});
}
else {
Sfdc.canvas.oauth.logout(); login.innerHTML = “Login”; Sfdc.canvas.byId(“oauth”).innerHTML = “”;
}
return false;
}
// Bootstrap the page once the DOM is ready. Sfdc.canvas(function() {
// On Ready…
var login = Sfdc.canvas.byId(“login”), loggedIn = Sfdc.canvas.oauth.loggedin(), token = Sfdc.canvas.oauth.token()
login.innerHTML = (loggedIn) ? “Logout” : “Login”; if (loggedIn) {
// Only displaying part of the OAuth token for better formatting. Sfdc.canvas.byId(“oauth”).innerHTML = Sfdc.canvas.oauth.token()
.substring(1,40) + “…”;
});
}
login.onclick=loginHandler;
</script>
<h1 id=”header”>Canvas OAuth App</h1>
<div>
access_token = <span id=”oauth”></span>
</div>
<div>
<a id=”login” href=”#”>Login</a><br/>
</div>
</body>
</html>

适用于Canvas应用程序的SAML单点登录

无论您是使用签名请求还是OAuth授权,都可以使用基于SAML的单点登录(SSO)为您的用户提供无缝的身份验证流程。您可以将Salesforce用作身份提供者或服务提供者。 SAML SSO使您能够通过SAML为您的用户提供自动身份验证,并通过签名请求对Salesforce进行身份验证。

SAML(安全断言标记语言)是一种基于XML的Web标准,用于在Web上进行用户身份验证,允许在两个域之间交换授权数据。使用此功能,您可以创建一个画布应用程序,该应用程序在用户打开时开始标准SAML身份验证流程。此过程完成后,用户将在Web应用程序中进行身份验证。

对于使用签名请求身份验证的画布应用程序,Canvas SDK中包含的两种方法使您的画布应用程序可以调用Salesforce以直接接收新的已签名请求,或使Salesforce能够将已签名的请求重新发布到Web应用程序端点。这导致完整的端到端身份验证流程。

refreshSignedRequest Method

通过回调返回新签名的请求。 SAML SSO流程完成后,您的应用可以调用此方法并接收新签名的请求。此方法适用于需要使用更多客户端JavaScript方法检索签名请求的开发人员。 (Canvas SDK将签名的请求发送到您的应用。)

repost Method

请求父窗口向您的画布应用程序启动POST,并使用刷新的签名请求重新加载应用程序页面。 SAML SSO流程完成后,您的应用可以调用此方法,并通过POST将新签名的请求发送到您的应用。此方法适用于希望使用更多服务器端方法检索已签名请求的开发人员。 (Salesforce将已签名的请求POST到您的服务器。)

注意:Lightning Platform社区上的画布应用程序不支持启动的SAML启动方法标识提供程序。

在您的画布应用程序中获取上下文

使用签名请求验证画布应用程序时,您将获得CanvasRequest对象(其中包含Context对象)作为画布应用程序URL的POST的一部分。如果您使用OAuth进行身份验证,或者想要调用获取上下文信息,则可以通过进行JavaScript调用来执行此操作。

以下代码示例是获取上下文的JavaScript调用示例。此代码创建一个带有文本“Get Context”的链接,然后调用Sfdc.canvas.client.ctx函数。

<script>
function callback(msg) {
if (msg.status !== 200) { alert(“Error: ” + msg.status); return;
}
alert(“Payload: “, msg.payload);
}
var ctxlink = Sfdc.canvas.byId(“ctxlink”); var client = Sfdc.canvas.oauth.client(); ctxlink.onclick=function() {
Sfdc.canvas.client.ctx(callback, client)};
}
</script>
<a id=”ctxlink” href=”#”>Get Context</a>

跨域XHR

Canvas应用程序加载在iFrame中的Salesforce页面上。因此,canvas应用程序(在其自己的域中)无法将XHR(XML HTTP请求)调用回到* .salesforce.com域。您可以开发和部署自己的代理作为SDK的一部分,但是,Canvas提供了用JavaScript编写的客户端代理。此代理使客户端XHR调用回Salesforce。

如果您使用客户端的此代理发出XHR请求,则API会将请求转发给外部iFrame,并代表您提交请求。请求完成后,SDK会使用结果调用客户端的回调函数。以下是一些如何进行XHR调用的示例:

  • 获取Chatter用户列表
  • 发布到Chatter Feed

注意:SDK支持跨域XHR调用,但不应将其用于进行同域调用。

获取Chatter用户列表

以下代码示例显示了一个返回Chatter用户列表的调用。

// Paste the signed request string into a JavaScript object for easy access. var sr = JSON.parse(‘<%=signedRequestJson%>’);
// Reference the Chatter user’s URL from Context.Links object. var chatterUsersUrl = sr.context.links.chatterUsersUrl;
// Make an XHR call back to salesforce through the supplied browser proxy. Sfdc.canvas.client.ajax(chatterUsersUrl,
{client : sr.client, success : function(data){
// Make sure the status code is OK. if (data.status === 200) {
// Alert with how many Chatter users were returned. alert(“Got back ” + data.payload.users.length +
” users”); // Returned 2 users
}
}});

发布到Chatter Feed

以下代码示例显示了将项目发布到上下文用户的Chatter摘要的调用。

var sr = JSON.parse(‘<%=signedRequestJson%>’);
// Reference the Chatter user’s URL from Context.Links object.
var url = sr.context.links.chatterFeedsUrl+”/news/”+sr.context.user.userId+”/feed-items”;
var body = {body : {messageSegments : [{type: “Text”, text: “Some Chatter Post”}]}};
Sfdc.canvas.client.ajax(url,
{client : sr.client, method: ‘POST’,
contentType: “application/json”, data: JSON.stringify(body), success : function(data) {
if (201 === data.status) { alert(“Success”);
}
}
});

调整画布应用程序的大小

Canvas提供了调整画布应用程序大小的方法。可以在SDK和此处找到这些方法的完整参考文档。

  • autogrow – 启动或停止计时器,该计时器检查画布iFrame的内容大小并调整帧。请参阅自动调整画布应用程序大小。
  • resize-通知父窗口调整画布iFrame的大小。请参阅显式调整画布应用程序大小。
  • size – 返回画布iFrame的当前大小。请参阅获取画布应用程序的大小。
  • subscribe – 订阅父事件。目前,canvas.scroll(父级)是canvas命名空间中唯一受支持的父事件。请参阅订阅父事件。

注意:要使调整大小函数与canvas应用程序一起使用,必须在与该应用程序关联的任何HTML页面的顶部声明DOCTYPE。 例如:<!DOCTYPE html>。

自动调整画布应用程序的大小

以下代码示例演示如何调用autogrow方法来调整画布应用程序的大小。 当您的内容更改大小时使用此方法,但您不确定何时。

注意:在Mozilla®Firefox®和Microsoft®InternetExplorer®中,如果内容大小减小,自动增长方法可能无法调整帧大小。 在这种情况下,您可以使用resize方法指定要将帧更改为的确切大小。

// Turn on auto grow with default settings. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.autogrow(sr.client);
});
// Turn on auto grow with polling interval of 100ms (milliseconds). Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.autogrow(sr.client, true, 100);
});
// Turn off auto grow. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.autogrow(sr.client, false);
});

显式调整画布应用程序的大小

以下代码示例演示如何调用resize方法来调整画布应用程序的大小。如果未指定高度和宽度参数,父窗口将尝试根据其内容确定画布应用程序的高度,然后相应地设置iFrame宽度和高度。

// Automatically determine the size. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.resize(sr.client);
});
// Set the height and width explicitly.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.resize(sr.client, {height : “1000px”, width : “900px”});
});
// Set only the height. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.resize(sr.client, {height : “1000px”});
});

获取画布应用程序的大小

以下代码示例演示如何调用size方法以获取画布应用程序的大小。 console.log函数输出帧大小,以便您在调整画布应用程序大小时可以看到大小更改。

// Get the canvas app sizes in the sizes object. var sizes = Sfdc.canvas.client.size();
console.log(“contentHeight; ” + sizes.heights.contentHeight);
console.log(“pageHeight; ” + sizes.heights.pageHeight);
console.log(“scrollTop; ” + sizes.heights.scrollTop);
console.log(“contentWidth; ” + sizes.widths.contentWidth);
console.log(“pageWidth; ” + sizes.widths.pageWidth);
console.log(“scrollLeft; ” + sizes.widths.scrollLeft);
// Resize the canvas app. Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’); Sfdc.canvas.client.autogrow(sr.client);
});

订阅父活动

以下代码示例演示如何调用subscribe方法,以便canvas应用程序可以订阅父事件。此示例处理用户在父窗口中滚动时触发的onscroll事件。

//Subscribe to the parent window onscroll event.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
// Capture the onScrolling event of the parent window
Sfdc.canvas.client.subscribe(sr.client,
{name : ‘canvas.scroll’, onData : function (event) {
console.log(“Parent’s contentHeight; ” + event.heights.contentHeight);
console.log(“Parent’s pageHeight; ” + event.heights.pageHeight);
console.log(“Parent’s scrollTop; ” + event.heights.scrollTop);
console.log(“Parent’s contentWidth; ” + event.widths.contentWidth);
console.log(“Parent’s pageWidth; ” + event.widths.pageWidth);
console.log(“Parent’s scrollLeft; ” + event.widths.scrollLeft);
}}
);
});

处理画布应用程序中的方向更改

通过方向事件,您可以在画布应用程序显示在移动设备上时处理方向更改。在您的画布应用程序订阅该事件后,只要父窗口触发window.orientation事件,就会触发该事件。该事件返回包含这些值的有效内容。

ValueDescription
clientHeight应用程序的高度(以像素为单位),特定于画布应用程序呈现的设备
clientWidth画布应用程序的宽度(以像素为单位),特定于画布应用程序渲染的设备
orientation包含其中一个值。
•0:横向到纵向
•90:纵向到横向
•-90:逆时针转动纵向到横向

以下代码示例显示如何订阅orientation事件。

// Capture the orientation event of the parent window.
Sfdc.canvas.client.subscribe(sr.client,
{name : ‘canvas.orientation’,
onData : function (event) {
console.log(“Parent’s orientation: ” + event.orientation + “Canvas app height: ” + event.clientHeight + “Canvas app width: ” + event. clientWidth);
}} );
});

注意:Windows手机不支持方向事件。

实现Canvas应用程序事件

事件提供了一种基于JavaScript的方式来在画布应用程序之间发送和接收事件。使用事件可在单个页面上启用多个画布应用程序之间的通信。

一种情况可能是您将两个自定义应用程序公开为画布应用程序的页面:旅行和费用应用程序以及批准应用程序。您可以创建一个事件,以便在费用报表的状态发生更改时,该事件会被引发并包含有关该费用报表的数据(采用JSON格式)。审批画布应用程序订阅该事件,并指定在引发事件时调用的函数。状态更改后,批准应用程序将接收事件并运行指定的功能。

Canvas提供了在canvas应用程序中实现自定义事件的方法。可以在SDK和此处找到这些方法的完整参考文档。

  • publish – 创建其他画布应用程序或Visualforce页面可以订阅的自定义事件。请参阅创建画布应用程序事件。
  • subscribe – 订阅父事件或自定义事件。此方法可用于订阅多个事件。请参阅订阅Canvas应用程序事件。
  • unsubscribe – 取消订阅父事件或自定义事件。此方法可用于取消订阅多个事件。请参阅取消订阅Canvas应用程序事件。

注意:subscribe和unsubscribe方法也可用于订阅单个Streaming API事件。

Canvas应用程序事件注意事项

在实现画布应用事件时,请记住以下注意事项:

  • 我们建议您在命名事件时使用命名空间,但这不是必需的。
  • 事件命名空间与Salesforce中的组织命名空间不同。但是,如果使用名称空间,我们建议您将事件名称空间与组织名称空间相同。
  • 命名空间必须是不包含句点的字符串。例如,my.name.space.statusChanged无效。具有命名空间的有效事件名称的示例是mynamespace.statusChanged。
  • 这些名称是保留的,不能用作命名空间:
    – canvas
    – chatter
    – force
    – publisher
    – salesforce
    – sfdc
  • 事件仅在同一页面上的画布应用程序之间起作用。如果Chatter选项卡上有画布应用程序,则该应用程序无法订阅Visualforce页面上画布应用程序发布的事件。
  • 您可以在订阅呼叫中订阅多个自定义事件。
  • 您可以在订阅呼叫中仅订阅一个Streaming API事件。
  • 您无法使用相同的订阅调用订阅自定义事件和Streaming API事件。
  • 如果在数组中定义具有相同名称的多个事件,则只有最后定义的事件可用。在此示例中,状态为协商的最后一个事件是使用的事件。
    对于Streaming API事件也是如此。

Sfdc.canvas.client.subscribe(sr.client, [
{
name :”mynamespace.statusChanged”, payload : {status : ‘Closed’}
},
{
name:”mynamespace.statusChanged”, payload : {status : ‘Negotiating’}
]);

创建Canvas应用程序事件

以下代码示例演示如何调用publish方法来创建画布应用程序事件。如果您正在使用命名空间,则事件名称必须以命名空间开头。例如,namespace.eventName。

Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.publish(sr.client,
{name : “mynamespace.statusChanged”, payload : {status : ‘Completed’}});
});

订阅Canvas应用程序事件

订阅自定义事件

以下代码示例演示如何调用subscribe方法来订阅画布应用程序事件。

// Subscribe to a custom event.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’); Sfdc.canvas.client.subscribe(sr.client,
{name : ‘mynamespace.statusChanged’, onData : function (event) { console.log(“Subscribed to custom event “, event);
}}
);
});

订阅多个自定义事件

以下代码示例演示如何调用subscribe方法以订阅多个画布应用程序事件。您订阅的事件可以位于不同的命名空间中,也可能没有命名空间。当画布应用程序订阅某个事件时,它会在事件(在另一个画布应用程序中)和一个函数(在订阅画布应用程序中)之间创建关联。

// Subscribe to multiple events.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.subscribe(sr.client, [
{name : ‘mynamespace.statusChanged’, onData : handler1},
{name : ‘anothernamespace.tripCancelled’, onData : handler2},
]);
});

使用旅行和费用以及审批画布应用示例,您的审批画布应用程序有两个功能:handler1和handler2。该画布应用程序然后订阅旅行和费用画布应用程序中的两个事件:mynamespace.statusChanged和mynamespace.tripCancelled。当批准应用程序收到mynamespace.statusChanged事件时,将调用函数handler1。当批准应用程序收到anothernamespace.tripCancelled事件时,将调用函数handler2。

取消订阅Canvas应用程序事件

取消订阅自定义事件

以下代码示例演示如何调用unsubscribe方法取消订阅画布应用程序事件。

// Unsubscribe from a custom event.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.unsubscribe(sr.client, {name : “mynamespace.statusChanged”});
});

取消订阅多个自定义事件

以下代码示例演示如何调用unsubscribe方法取消订阅多个canvas应用程序事件。您订阅的事件可以位于不同的命名空间中,也可能没有命名空间。

// Unsubscribe from multiple events.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
Sfdc.canvas.client.unsubscribe(sr.client, [‘mynamespace.statusChanged’,
“anothernamespace.tripCancelled”]);
});

在Canvas应用程序中使用Streaming API

Canvas提供了一个事件和方法,使画布应用程序能够侦听Streaming API通知。

  • sfdc.streamingapi-JavaScript事件,您创建并与PushTopic定义的Streaming API通道关联。请参阅使用Streaming API事件。
  • subscribe – 订阅您定义的sfdc.streamingapi事件。请参阅订阅流式API事件。
  • unsubscribe – 取消订阅sfdc.streamingapi事件。请参阅从Streaming API事件中取消订阅。

使用Streaming API事件

Canvas SDK包含一个名为sfdc.streamingapi的事件,它允许您在画布应用程序中定义事件并将该事件与Streaming API通道相关联。然后,您使用subscribe方法订阅该事件并接收Streaming API通知。

例如,在Salesforce中,您可以创建一个Streaming API通道,该通道在更新InvoiceStatement并且Status更改为Closed时接收通知。在您的画布应用程序中,您可以创建与该频道关联的事件并订阅它。在Salesforce中,只要关闭发票声明,激活的画布应用程序就会收到通知,并可以执行操作,例如向用户显示消息。

以下是定义Streaming API事件时的一些注意事项:

  • 事件采用包含PushTopic名称的单个参数。
  • PushTopic名称必须以“/ topic /”开头。

{name:”sfdc.streamingapi”, params:{topic:”/topic/myPushTopicName”}}

订阅Streaming API事件

此代码示例演示如何调用subscribe方法,以便canvas应用程序可以订阅Streaming API事件。订阅事件时,可以调用用于订阅画布应用事件的标准sfdc.canvas.client.subscribe方法。调用subscribe方法时,必须传入客户端和Streaming API事件。只有打开并订阅该事件的画布应用才能接收流式API通知。

在此示例中,onComplete方法指定在代码成功订阅事件后运行的函数。该
onData方法指定事件收到Streaming API通知时运行的函数。

// Subscribe to Streaming API events.
// The PushTopic to subscribe to must be passed in.
// The ‘onComplete’ method may be defined,
// and will fire when the subscription is complete.
Sfdc.canvas(function() {
sr = JSON.parse(‘<%=signedRequestJson%>’);
var handler1 = function(){ console.log(“onData done”);},
handler2 = function(){ console.log(“onComplete done”);};
Sfdc.canvas.client.subscribe(sr.client,
{name : ‘sfdc.streamingapi’, params:{topic:”/topic/InvoiceStatements”}}, onData : handler1, onComplete : handler2}
);
});

调用subscribe方法时,会进行REST调用以确保画布应用程序具有连接到Streaming API所需的OAuth范围。因此,每次画布应用程序订阅Streaming API事件时,都会使用一个API调用,并根据组织的API请求总限制进行计数。 canvas应用程序至少需要“访问和管理您的数据(API)”OAuth范围才能连接到Streaming API。

如果对subscribe方法的调用成功,则调用onComplete方法,其有效负载为
{成功:真的,拉手:拉手}。句柄是一个数组,其中包含要订阅的Streaming API通道的名称,而subscriptionId是一个包含唯一ID的整数。例如,[“/ topics / InvoiceStatements”,subscriptionId]。如果对subscribe方法的调用失败,则调用onComplete方法,其有效负载为{success:false,errorMessage:msg}。 msg是一个包含错误原因的字符串。
要接收Streaming API通知,您必须创建PushTopic定义的通道。有关更多信息,请参阅Streaming API开发人员指南中的“步骤2:创建PushTopic”。

取消订阅Streaming API事件

此代码示例演示如何调用unsubscribe方法,以便画布应用程序可以取消订阅Streaming API事件。

在Canvas应用程序中调试

您必须使用Chrome,Firefox或Safari才能执行这些步骤。
使用Canvas SDK时,您可以启用调试模式以简化故障排除。

  1. 在浏览器中打开画布应用程序,打开控制台。
  2. 键入Sfdc.canvas.console.enable();并按回车键。
  3. 刷新浏览器。

Use data from this screen to debug canvas-related problems.

使用此屏幕中的数据来调试与画布相关的问题。

在Canvas App中使用e <select> 标签

如果您在画布Web应用程序中使用HTML <select>标签并打算在Salesforce应用程序中使用画布应用程序,请记住,某些设备上的行为可能无法预测。 考虑使用jQuery或CSS替代方法来使用<select>。