Salesforce Lightning Component通过Lightning Out暴露给外部服务

前言】:在实施Service Cloud Embeded Service时候,自定义Pre-Chat Form,Chat Window等需要用到Lightning Component,所以之前使用VFP + Site这一套方案实施的Live Agent需要升级为Lightning Component + Embeded Service + Site了,那么第一步需要解决的是如何将Lightning Component通过Site暴露给外部服务?经查阅资料,发现Lightning Out可以在VFP中引用Lightning Component。本篇将以Demo形式展示Lightning Out这一技术,并提供授权和非授权两种使用Lightning Out的方法

应用场景举例】:
1、使用Aura Component写了Trailhead排名应用,想暴露给参与者访问;
2、通过git应用托管服务,将sf资源暴露给end user,Sample
3、之前在git上找Custom Email Composer资源时,看到有作者把演示链接放在里面,或许我们也可以通过这个技术更形象直观的让visitor了解这个代码是在干些啥。

技术骨架】:

Demo演练】:
目标:我们在Org1有2个Aura Component,分别用来展示10个Accounts和Contacts,我们将2个组件通过resource的形式放入同一个Dependency App里,然后通过Lightning Out JS将组件内容渲染到VFP指定的div盒子,最终通过Site将VFP暴露给外部(免登陆访问SF资源)。

效果1:dependency app通过实现ltng:allowGuestAccess接口,以Guest User Context供外部User访问

代码示例:
Lightning Component部分:
a. Component & Controller.js

<!-- DisplayTenAccountsComp.cmp -->
<aura:component controller="LightningOutCtrl">
    <aura:attribute name="accList" type="Account[]" access="private"/>
    <aura:attribute name="userInfo" type="String" access="private"/>
    <aura:attribute name="conList" type="Contact[]" access="private"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <div>
    	<p>Current UserInfo: {!v.userInfo}</p>
    </div>
    <br/>
    <ul>
    	<aura:iteration items="{!v.accList}" var="acc">
            <li>{!acc.Id} · {!acc.Name}</li>
        </aura:iteration>
    </ul>
    <br/>
	<ul>
    	<aura:iteration items="{!v.conList}" var="con">
            <li>{!con.Id} · {!con.Name} · {!con.Account.Name}</li>
        </aura:iteration>
    </ul>
</aura:component>

<!-- DisplayTenAccountsCompController.js -->
({
	doInit : function(component, event, helper) {
		let action = component.get("c.getTenAccounts");
        action.setCallback(this, function(response) {
            const data = response.getReturnValue();
            const dataSize = data.length;
            console.log('dataSize: ' + dataSize);
            component.set('v.accList', data);
        });
        $A.enqueueAction(action);
        
        let action1 = component.get("c.getTenContacts");
        action1.setCallback(this, function(response) {
            const data = response.getReturnValue();
            const dataSize = data.length;
            console.log('dataSize: ' + dataSize);
            component.set('v.conList', data);
        });
        $A.enqueueAction(action1);
        
        let action2 = component.get("c.getUserInfo");
        action2.setCallback(this, function(response) {
            const data = response.getReturnValue();
            component.set('v.userInfo', data);
        });
        $A.enqueueAction(action2);
	}
})
<!-- DisplayTenContactsComp.cmp -->
<aura:component controller="LightningOutCtrl">
    <aura:attribute name="conList" type="Contact[]" access="private"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
	<ul>
    	<aura:iteration items="{!v.conList}" var="con">
            <li>{!con.Id} · {!con.Name}</li>
        </aura:iteration>
    </ul>
</aura:component>

<!-- DisplayTenContactsCompController.js -->
({
	doInit : function(component, event, helper) {
        let action1 = component.get("c.getTenContacts");
        action1.setCallback(this, function(response) {
            const data = response.getReturnValue();
            const dataSize = data.length;
            console.log('dataSize: ' + dataSize);
            component.set('v.conList', data);
        });
        $A.enqueueAction(action1);
	}
})

b. Apex Controller

public without sharing class LightningOutCtrl {
	@AuraEnabled
    public static List<Account> getTenAccounts() {
        return [SELECT Id, Name FROM Account LIMIT 10];
    }
    
    @AuraEnabled
    public static List<Contact> getTenContacts() {
        return [SELECT Id, Name, Account.Name FROM Contact LIMIT 10];
    }
    
    @AuraEnabled
    public static String getUserInfo() {
        return '{"UserName": '+UserInfo.getUserName()+', "Name": '+UserInfo.getName()+', "Email": '+UserInfo.getUserEmail()+'}';
        // return [SELECT Id, Name, UserName, Email, EmailEncodingKey, Profile.Name, UserRole.Name FROM User WHERE Id =:UserInfo.getUserId()];
    }
}

c. Dependency App(此处实现了ltng:allowGuestAccess接口用于暴露服务给外部用户,若未声明需要通过配置Connected App,然后在第三方应用通过账密获取access token访问SF资源

<!-- ALightningOutApp.app -->
<aura:application extends="ltng:outApp" access="global" implements="ltng:allowGuestAccess">
    <aura:dependency resource="c:DisplayTenAccountsComp" />
    <aura:dependency resource="c:DisplayTenContactsComp" />
</aura:application>

d. VFP

<!-- Component和VFP不同源 -->
<apex:page showHeader="false" sidebar="false">
    <script src="https://yoursitedomain.force.com/site/lightning/lightning.out.js"></script>
    <center><apex:outputText style="font-size: 18px; color: #f0f" value="Hello, this is my Lightning Out Demo" /></center>
    <div id='accList'></div>
    <br/>
    <div id='conList'></div>
    
    <script>
        // dependencyApp
        $Lightning.use('c:ALightningOutApp', function() {
                // component used in dependencyApp
                $Lightning.createComponent('c:DisplayTenAccountsComp', 
                                           {},
                                           'accList',
                                           function() {
                                               //do some stuff
                                               console.log('accList has been created...');
                                           });
                // component used in dependencyApp
                $Lightning.createComponent('c:DisplayTenContactsComp',
                                           {},
                                           'conList',
                                           function() {
                                               //do some stuff
                                               console.log('conList has been created...');
                                           });
            }, 'https://yoursitedomain.force.com/site'
        );
    </script>
</apex:page>

<!-- Component和VFP同源 -->
<apex:page showHeader="false" sidebar="false">
    <apex:includeLightning />
    <center><apex:outputText style="font-size: 18px; color: #f0f" value="Hello, this is my Lightning Out Demo" /></center>
    <div id='accList'></div>
    <br/>
    <div id='conList'></div>
    
    <script>
        $Lightning.use('c:ALightningOutApp', function() {
            // component used in dependencyApp
            $Lightning.createComponent('c:DisplayTenAccountsComp', 
                                       {},
                                       'accList',
                                       function() {
                                           //do some stuff
                                           console.log('accList has been created...');
                                       });
            // component used in dependencyApp
            $Lightning.createComponent('c:DisplayTenContactsComp',
                                       {},
                                       'conList',
                                       function() {
                                           //do some stuff
                                           console.log('conList has been created...');
                                       });
        });
    </script>
</apex:page>

效果2:org1包含lightning组件和数据资产,org2模拟第三方应用,我们拟在org2中通过Site以org1 authToken的形式访问org1的lightning component和data

代码示例:

这里与上边通过实现ltng:allowGuestAccess接口不同之处有2点:
a. AlightningOutApp.app里边无需实现guest接口:

<!-- <aura:application extends="ltng:outApp" access="global" implements="ltng:allowGuestAccess"> -->
<aura:application extends="ltng:outApp" access="global">
    <aura:dependency resource="c:DisplayTenAccountsComp" />
    <aura:dependency resource="c:DisplayTenContactsComp" />
</aura:application>

b. org2里边vfp代码在引用Lightning Out JS库和使用库提供的Use方法传参有差异(注意下面代码中一定要使用org1 lightning URI,classic的URI会报错):

<apex:page showHeader="false" sidebar="false">
    <!-- <apex:includeLightning/> -->
    <script src="https://org1.lightning.force.com/lightning/lightning.out.js"></script>
    <center><apex:outputText style="font-size: 18px; color: #f0f" value="Hello, this is my Lightning Out Demo" /></center>
    <div id='accList'></div>
    <br/>
    <div id='conList'></div>
    
    <script>
        // dependencyApp
        $Lightning.use('c:ALightningOutApp', function() {
                // component used in dependencyApp
                $Lightning.createComponent('c:DisplayTenAccountsComp', 
                                           {},
                                           'accList',
                                           function() {
                                               //do some stuff
                                               console.log('accList has been created...');
                                           });
                // component used in dependencyApp
                $Lightning.createComponent('c:DisplayTenContactsComp',
                                           {},
                                           'conList',
                                           function() {
                                               //do some stuff
                                               console.log('conList has been created...');
                                           });
            }, 'https://org1.lightning.force.com', 'access_token'
        );
    </script>
</apex:page>

Q&A】:
Q1. 如何获取access_token?
A1. 参见Using Advanced REST Client to test REST Request - step by step

特别注意】:
1. 如果不同源,需要在Component所在的Org1加CORS [Org2的community uri / classic uri](同源不存在跨域,不需要加);
2. 必须在site profile里启用vfp(同源时),apex和OLS(缺一不可);
    官方文档说lightning out not enforce OLS and FLS, 实验后持保留意见
3. 如果查询org级别数据,apex必须使用without sharing,FLS加不加无所谓;

否则,console可能会出现服务器500错误或如下错误:
Access to XMLHttpRequest at 'https://yoursitedomain1.force.com/site1/ALightningOutApp.app?aura.format=JSON&aura.formatAdapter=LIGHTNING_OUT' from origin 'https://yoursitedomain2.force.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值