本篇参考:

https://developer.salesforce.com/docs/atlas.en-us.224.0.api_rest.meta/api_rest/resources_composite_composite.htm

https://developer.salesforce.com/docs/atlas.en-us.224.0.api_rest.meta/api_rest/resources_composite_sobject_tree.htm

https://developer.salesforce.com/docs/atlas.en-us.224.0.api_rest.meta/api_rest/resources_composite_sobjects_collections_update.htm

https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch

salesforce零基础学习(一百零三)项目中的零碎知识点小总结(五)

https://jeremyliberman.com/2019/02/11/fetch-has-been-blocked-by-cors-policy.html

我们在学习LWC的时候,使用 wire adapter特别爽,比如 createRecord / updateRecord,按照指定的格式,在前端就可以直接将数据的创建更新等操作搞定了,lwc提供的wire adapter使用的是 User Interface API来实现。当然,人都是很贪婪的,当我们对这个功能使用起来特别爽的时候,也在疑惑为什么没有批量的创建和更新的 wire adapter,这样我们针对一些简单的数据结构,就不需要写apex class,这样也就不需要维护相关的test class,不需要考虑部署的时候漏资源等等。那么,针对批量数据的场景,是否有什么方式可以不需要apex,直接前台搞定吗?当然可以,我们可以通过调用标准的rest api接口去搞定。

ContactController.cls

  1. public with sharing class ContactController {
  2.  
  3. @AuraEnabled(cacheable=true)
  4. public static List<Contact> getContacts() {
  5. return [
  6. SELECT AccountId, Id, FirstName, LastName, Title, Phone, Email
  7. FROM Contact limit 10
  8. ];
  9. }
  10.  
  11. @AuraEnabled
  12. public static string updateContacts(Object data) {
  13. List<Contact> contactsForUpdate = (List<Contact>) JSON.deserialize(
  14. JSON.serialize(data),
  15. List<Contact>.class
  16. );
  17. try {
  18. update contactsForUpdate;
  19. return 'Success: contacts updated successfully';
  20. }
  21. catch (Exception e) {
  22. return 'The following exception has occurred: ' + e.getMessage();
  23. }
  24. }
  25. }

datatableUpdateExample.html

  1. <template>
  2. <lightning-card title="Datatable Example" icon-name="custom:custom63">
  3. <div class="slds-m-around_medium">
  4. <template if:true={contact.data}>
  5. <lightning-datatable
  6. key-field="Id"
  7. data={contact.data}
  8. columns={columns}
  9. onsave={handleSave}
  10. draft-values={draftValues}>
  11. </lightning-datatable>
  12. </template>
  13. <template if:true={contact.error}>
  14. <!-- handle Apex error -->
  15. </template>
  16. </div>
  17. </lightning-card>
  18. </template>

datatableUpdateExample.js

  1. import { LightningElement, wire, api } from 'lwc';
  2. import getContacts from '@salesforce/apex/ContactController.getContacts';
  3. import { refreshApex } from '@salesforce/apex';
  4.  
  5. import { ShowToastEvent } from 'lightning/platformShowToastEvent';
  6.  
  7. import updateContacts from '@salesforce/apex/ContactController.updateContacts';
  8.  
  9. const COLS = [
  10. { label: 'First Name', fieldName: 'FirstName', editable: true },
  11. { label: 'Last Name', fieldName: 'LastName', editable: true },
  12. { label: 'Title', fieldName: 'Title' },
  13. { label: 'Phone', fieldName: 'Phone', type: 'phone' },
  14. { label: 'Email', fieldName: 'Email', type: 'email' }
  15. ];
  16. export default class DatatableUpdateExample extends LightningElement {
  17. columns = COLS;
  18. draftValues = [];
  19.  
  20. @wire(getContacts)
  21. contact;
  22.  
  23. async handleSave(event) {
  24. const updatedFields = event.detail.draftValues;
  25.  
  26. await updateContacts({data: updatedFields})
  27. .then(result => {
  28. this.dispatchEvent(
  29. new ShowToastEvent({
  30. title: 'Success',
  31. message: 'Contact updated',
  32. variant: 'success'
  33. })
  34. );
  35.  
  36. // Display fresh data in the datatable
  37. refreshApex(this.contact).then(() => {
  38. this.draftValues = [];
  39. });
  40. }).catch(error => {
  41. console.log(JSON.stringify(error));
  42. if(error.body) {
  43. console.log(JSON.stringify(error.body));
  44. } else if(error.detail) {
  45. console.log(JSON.stringify(error.detail));
  46. }
  47. this.dispatchEvent(
  48. new ShowToastEvent({
  49. title: 'Error updating or refreshing records',
  50. //message: error.body.message,
  51. variant: 'error'
  52. })
  53. );
  54. });
  55. }
  56. }

结果展示:

点击以后

我们在上一篇讲述了标准的rest api,那OK,我们可以尝试不适用后台apex方式去搞定,而是在前台通过rest api去玩一下,说到做到,开弄。后台 apex增加获取session的方法

  1. public with sharing class ContactController {
  2.  
  3. @AuraEnabled(cacheable=true)
  4. public static String getSessionId() {
  5. return UserInfo.getSessionId();
  6. }
  7.  
  8. @AuraEnabled(cacheable=true)
  9. public static List<Contact> getContacts() {
  10. return [
  11. SELECT AccountId, Id, FirstName, LastName, Title, Phone, Email
  12. FROM Contact limit 10
  13. ];
  14. }
  15. }

前端 html / javascript也同样的改造一下

  1. import { LightningElement, wire, api, track } from 'lwc';
  2. import getContacts from '@salesforce/apex/ContactController.getContacts';
  3. import { refreshApex } from '@salesforce/apex';
  4.  
  5. import { ShowToastEvent } from 'lightning/platformShowToastEvent';
  6.  
  7. import updateContacts from '@salesforce/apex/ContactController.updateContacts';
  8. import getSessionId from '@salesforce/apex/ContactController.getSessionId';
  9.  
  10. const COLS = [
  11. { label: 'First Name', fieldName: 'FirstName', editable: true },
  12. { label: 'Last Name', fieldName: 'LastName', editable: true },
  13. { label: 'Title', fieldName: 'Title' },
  14. { label: 'Phone', fieldName: 'Phone', type: 'phone' },
  15. { label: 'Email', fieldName: 'Email', type: 'email' }
  16. ];
  17. export default class DatatableUpdateExample extends LightningElement {
  18. columns = COLS;
  19. draftValues = [];
  20. @track isShowSpinner = false;
  21. @track sessionId;
  22.  
  23. @wire(getContacts)
  24. contact;
  25.  
  26. handleSave(event) {
  27. this.isShowSpinner = true;
  28. const updatedFields = event.detail.draftValues;
  29. updatedFields.forEach(item => {
  30. item.attributes = {"type" : "Contact"};
  31. });
  32. let requestBody = { "allOrNone": false, "records": updatedFields };
  33. console.log(JSON.stringify(updatedFields));
  34. getSessionId()
  35. .then(result => {
  36. this.sessionId = result;
  37. fetch('/services/data/v50.0/composite/sobjects/',
  38.         {
  39.             method: "PATCH",
  40.             body: JSON.stringify(requestBody),
  41.             headers: {
  42. "Content-Type": "application/json",
  43. "Authorization": "Bearer " + this.sessionId
  44. }
  45. }).then((response) => {
  46. //TODO 可以通过 status code判断是否有超时或者其他异常,如果是200,则不管更新成功失败,至少response回来
  47. console.log(response.status);
  48. return response.json(); // returning the response in the form of JSON
  49. })
  50. .then((jsonResponse) => {
  51. console.log('jsonResponse ===> '+JSON.stringify(jsonResponse));
  52. if(jsonResponse) {
  53. jsonResponse.forEach(item => {
  54. if(item.success) {
  55. console.log(item.id + 'update success');
  56. } else {
  57. console.log(item.id + 'update failed');
  58. }
  59. })
  60. }
  61. refreshApex(this.contact).then(() => {
  62. this.draftValues = [];
  63. });
  64. this.isShowSpinner = false;
  65.  
  66. })
  67. .catch(error => {
  68. console.log('callout error ===> '+JSON.stringify(error));
  69. this.isShowSpinner = false;
  70. })
  71. })
  72. .catch(error => {
  73. //TODO
  74. console.log('callout error ===> '+JSON.stringify(error));
  75. this.isShowSpinner = false;
  76. })
  77.  
  78. }
  79. }

对应html

  1. <template>
  2. <lightning-card title="Datatable Example" icon-name="custom:custom63">
  3. <div class="slds-m-around_medium">
  4. <template if:true={contact.data}>
  5. <lightning-datatable
  6. key-field="Id"
  7. data={contact.data}
  8. columns={columns}
  9. onsave={handleSave}
  10. draft-values={draftValues}>
  11. </lightning-datatable>
  12. </template>
  13. <template if:true={contact.error}>
  14. <!-- handle Apex error -->
  15. </template>
  16. </div>
  17. </lightning-card>
  18. <template if:true={isShowSpinner}>
  19. <lightning-spinner alternative-text="Loading" size="medium"></lightning-spinner>
  20. </template>
  21. </template>

运行展示:通过下图可以看到报错了CORS相关的错误,因为跨域进行了请求,这种情况的处理很单一也不麻烦,只需要 setup去配置相关的CORS以及CSP trust site肯定没有错

下图是配置的CSP 以及CORS

但是很遗憾的是,即使配置了这些内容,还是不可以。也征集了群里大神的各种建议意见,各种尝试扩充了 request header,发现还是不行。因为准备备考integration,所以也就暂时搁置了这个尝试。周末时间相对充裕,不太甘心的忽然想到了一个事情,不要只看 console的报错,查看一下network是否有什么有用的信息。

通过这个截图我们可以看出来,这个http 操作有三次的请求,第一次是跨域的检查,request method是option,感兴趣的可以自己查看

进行了错误的这次请求的展开,将 response内容展开,发现了问题

好家伙,尽管console报错是CORS,但是其实这个问题的rootcause是 请求返回的code是401未授权,打开 rest api 文档查看一下

破案了,后台通过 UserInfo.getSessionId获取的session信息无法用于REST API的授权,这里就会有一个疑问,因为艾总发过来了一个VF的demo,是可以通过rest去调用的,难道是vf / lex哪里有区别,或者session有区别?

然后我就做了一个vf去打印一下session信息以及通过apex在lex展示session信息,发现visualforce page通过 GETSESSIONID或者 {!$Api.Session_ID}获取的session id信息和apexclass获取的session id不一致,并且 vf 获取的是可用的。OK,找到了解决方案以后,进行demo的bug fix。

GenerateSessionId.page

  1. <apex:page contentType="application/json">
  2. {!$Api.Session_ID}
  3. </apex:page>

ContactController: 只需要修改 getSessionId方法即可

  1. @AuraEnabled(cacheable=true)
  2. public static String getSessionId() {
  3. return Page.GenerateSessionId.getContent().toString().trim();
  4. }

验证:搞定

总结:篇中只展示了一下通过 REST API去批量操作数据的可行性,仅作为一个简单的demo很多没有优化,异常处理,错误处理等等。而且对数据量也有要求,200以内。如果感兴趣的小伙伴欢迎自行去进行优化,希望以后有相关需求的小伙伴可以避免踩坑。篇中有错误的地方欢迎指出,有不懂欢迎留言。

Salesforce LWC学习(三十五) 使用 REST API实现不写Apex的批量创建/更新数据的更多相关文章

  1. Salesforce LWC学习(三十九) lwc下quick action的recordId的问题和解决方案

    本篇参考: https://developer.salesforce.com/docs/component-library/bundle/force:hasRecordId/documentation ...

  2. Salesforce LWC学习(三十) lwc superbadge项目实现

    本篇参考:https://trailhead.salesforce.com/content/learn/superbadges/superbadge_lwc_specialist 我们做lwc的学习时 ...

  3. Salesforce LWC学习(三十六) Quick Action 支持选择 LWC了

    本篇参考: https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.use_quick_act ...

  4. Salesforce LWC学习(三十四) 如何更改标准组件的相关属性信息

    本篇参考: https://www.cnblogs.com/zero-zyq/p/14548676.html https://www.lightningdesignsystem.com/platfor ...

  5. Salesforce LWC学习(三十八) lwc下如何更新超过1万的数据

    背景: 今天项目组小伙伴问了一个问题,如果更新数据超过1万条的情况下,有什么好的方式来实现呢?我们都知道一个transaction只能做10000条DML数据操作,那客户的操作的数据就是超过10000 ...

  6. Salesforce LWC学习(二十五) Jest Test

    本篇参看: https://trailhead.salesforce.com/content/learn/modules/test-lightning-web-components https://j ...

  7. Salesforce LWC学习(三十二)实现上传 Excel解析其内容

    本篇参考:salesforce lightning零基础学习(十七) 实现上传 Excel解析其内容 上一篇我们写了aura方式上传excel解析其内容.lwc作为salesforce的新宠儿,逐渐的 ...

  8. Salesforce LWC学习(四十) dynamic interaction 浅入浅出

    本篇参考: Configure a Component for Dynamic Interactions in the Lightning App Builder - Salesforce Light ...

  9. 微信小程序把玩(三十五)Video API

    原文:微信小程序把玩(三十五)Video API 电脑端不能测试拍摄功能只能测试选择视频功能,好像只支持mp4格式,值得注意的是成功之后返回的临时文件路径是个列表tempFilePaths而不是tem ...

随机推荐

  1. jQ的隐式迭代和设置样式属性

    jQ中的隐式迭代 意义:不需要原生迭代了,在jQ内部自动帮你实现了循环 代码实现: let arr = document.querySelectorAll('li') for(let i = 0;i ...

  2. java基础——数组及其应用

    数组 数组时相同类型数据的有序集合 数组描述的时相同类型的若干数据,按照一个定的先后次序排列组合而成 其中,每一个数据成为数组元素,每个数组元素可以通过一个下标来访问他们 数组的声明&创建 首 ...

  3. calico NetworkPolicy on kubernetes

    什么是网络策略 在Kubernetes平台中,要实现零信任网络的安全架构,Calico与istio是在Kubernetes集群中构建零信任网络必不可少的组件. 而建立和维护整个集群中的"零信 ...

  4. [Python] 可变/不可变类型 & 参数传递

    与c/c++不同,Python/Java中的变量都是引用类型,没有值类型 Python赋值语句由三部分构成,例如:   int   a  = 1 类型 标识 值 标识(identity):用于唯一标识 ...

  5. 【转载】windows linux cent 7 制作U盘 启动盘

    1 镜像iso文件存放在linux环境下用dd if=/dev/sdb of=/镜像存放路径/镜像iso文件 bs=1M u盘的盘符是/dev/sdb 2 镜像iso文件存放在windows环境下ul ...

  6. 【转载】CentOS 7 系统区域(语言)和键盘设置

    CentOS 7 系统区域(语言)和键盘设置   即使是在window中,平常说的语言设置这一项也是归类为系统区域,CentOS可以通过修改/etc/locale.conf配置文件或使用localec ...

  7. -bash: $'\201ccd': δ 的错误是linux编码问题(Centos7)

    如果目录是中文目录,你的编码为: [root@dbbd-api01 ~]# cat /etc/locale.conf LANG=zh_CN.GB18030 [root@dbbd-api01 ~]# 那 ...

  8. JDK5.0新特性 (Day_07)

      JDK5.0新特性   目录 静态导入 自动装箱/拆箱 for-each循环 可变参数 枚举 JDK 5.0 新特性简介 JDK 5.0 的一个重要主题就是通过新增一些特性来简化开发,这些特性包括 ...

  9. mysql的日志文件及其作用

    MySQL中有七种日志文件,分别是: 重做日志(redo log) 回滚日志(undo log) 二进制日志(binlog) 中继日志(relay log) 错误日志(errorlog) 慢查询日志( ...

  10. SpringBoot2 集成测试组件,七种测试手段对比

    一.背景描述 在版本开发中,时间段大致的划分为:需求,开发,测试: 需求阶段:理解需求做好接口设计: 开发阶段:完成功能开发和对接: 测试上线:自测,提测,修复,上线: 实际上开发阶段两个核心的工作, ...