注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

微软MVP罗勇的博客

微软MVP罗勇,www.luoyong.me

 
 
 

日志

 
 

使用JS通过Web API执行批量操作,多个操作是一个事务!  

2016-11-05 16:58:51|  分类: CRM客户端开发 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复235或者20161105可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me 。
Dynamics CRM的Web API新增了一个功能,就是 Execute batch operations using the Web API ,我也参考了
pham.hongnghiep 的文章 involve Batch Operation CRM 2016 Web Api ,这个功能在某些情况下可能很有用。因为它能通过JS调用一次Web API执行多个操作(这个操作是指一个动作,比如新增一条记录,删除一条记录等,不是流程类型中的操作),而且这多个操作是同一个事物,都成功或者都失败!这个功能和我前面文章 执行插件的替代方式:用JS调用操作 功能类似,但是不需要创建操作,这个是优点,缺点是构造这个POST请求的内容和解析返回的内容比较复杂,都是文本,不能方便的转换成JSON。
为了解释这个概念,我虚拟出一个需求:
1)创建一个罗勇测试实体(ly_test)记录及两个罗勇测试辅助实体(ly_testsub)记录,
2)为没有关联关系的记录,某个固定的客户增加一个任务
3更新本批次创建的测试实体的一个字段的值,注意$1的运用,它是引用同一个change set中的增加了Content-ID头创建记录的URL)。
写代码的时候要注意:
1. changeset 结尾的时候有 -- ,开始的时候则没有,batch也是。
2. 创建父子关系的实体记录不要通过batch,通过深度创建即可,可有参考我的博文:Dynamics CRM 中Web API中的深度创建(Deep Insert) .
3. $1等的引用不能放到request body中的json中,反正我试了不行,也可能是我没有找到正确的方法。
4. 一个changeset中的一个操作是从 --changeset 部分开始,到下一个--changeset,注意其中Content-ID后面要加空行,真正请求的内容前面要加空行,也就是 Content-Type: application/json;type=entry 后面。
下面奉上一个完整的代码:

function submit() {
var clientURL = Xrm.Page.context.getClientUrl();
var batchId = getRandomString(12);
var changesetId = getRandomString(12);
var requestMsg = ["--batch_" + batchId];
requestMsg.push("Content-Type: multipart/mixed;boundary=changeset_" + changesetId);
requestMsg.push("");
requestMsg.push("--changeset_" + changesetId);
requestMsg.push("Content-Type: application/http");
requestMsg.push("Content-Transfer-Encoding:binary");
requestMsg.push("Content-ID: 1");
requestMsg.push("");
requestMsg.push("POST " +clientURL + "/api/data/v8.1/ly_tests HTTP/1.1");
requestMsg.push("Content-Type: application/json;type=entry");
requestMsg.push("");//注意这里要加空行
var subMsg1 = {};
subMsg1.ly_name = "批量操作创建的罗勇测试记录";
subMsg1["ly_Lookup@odata.bind"] = "/accounts(CE23165A-3AA3-E511-80C7-000D3A807EC7)";
subMsg1["ly_ly_test_ly_testsub_Test"] = [];
subMsg1["ly_ly_test_ly_testsub_Test"].push({"ly_name":"批量操作创建的罗勇测试辅助实体记录1"});
subMsg1["ly_ly_test_ly_testsub_Test"].push({"ly_name":"批量操作创建的罗勇测试辅助实体记录2"});
requestMsg.push(JSON.stringify(subMsg1));
requestMsg.push("--changeset_" + changesetId);
requestMsg.push("Content-Type: application/http");
requestMsg.push("Content-Transfer-Encoding:binary");
requestMsg.push("Content-ID: 2");
requestMsg.push("");//注意这里要加空行
requestMsg.push("POST " + clientURL + "/api/data/v8.1/tasks HTTP/1.1");
requestMsg.push("Content-Type: application/json;type=entry");
requestMsg.push("");//注意这里要加空行
requestMsg.push("{'subject':'批量操作创建的任务','regardingobjectid_account_task@odata.bind':'https://demo.luoyong.me/api/data/v8.1/accounts(C223165A-3AA3-E511-80C7-000D3A807EC7)'}");
requestMsg.push("--changeset_" + changesetId);
requestMsg.push("Content-Type: application/http");
requestMsg.push("Content-Transfer-Encoding:binary");
requestMsg.push("Content-ID: 3");
requestMsg.push("");
requestMsg.push("PATCH $1 HTTP/1.1");
requestMsg.push("Content-Type: application/json;type=entry");
requestMsg.push("");//注意这里要加空行
requestMsg.push("{'ly_singlelinetext':'更新了批量操作创建的罗勇测试实体记录'}");
requestMsg.push("--changeset_" + changesetId + "--");//changeset结束这里前面不要加空行
requestMsg.push("");//注意这里要加空行
requestMsg.push("--batch_" + batchId + "--");//batch结束前面加空行
executeBatch(clientURL, batchId, requestMsg.join("\n"), function(responseText){
Xrm.Utility.alertDialog("执行后返回status=200,也有可能有错误:" + responseText);
}, function (responseText) {
Xrm.Utility.alertDialog("执行出错:" + responseText);
});
}

function executeBatch(clientURL,batchId,requestMsg,successCallback,errorCallback) {
var req = new XMLHttpRequest()
req.open("POST", encodeURI(clientURL + "/api/data/v8.1/$batch", true));//true是异步请求,false是同步请求
req.setRequestHeader("Content-Type", "multipart/mixed;boundary=batch_" + batchId);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {//204代表成功无返回值
successCallback(this.responseText);
}
else {
errorCallback(this.responseText);
}
}
};
req.send(requestMsg);
}

function getRandomString(len, charSet) {
charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var randomString = '';
for (var i = 0; i < len; i++) {
var randomPoz = Math.floor(Math.random() * charSet.length);
randomString += charSet.substring(randomPoz, randomPoz + 1);
}
return randomString;
}


下面是如果使用fiddler模拟的话是如下操作:
1) POST到的URL是 https://demo.luoyong.me/api/data/v8.1/$batch
2) 请求头是:注意最后一个Authorization: Bearer 是认证信息,根据实际情况需要的加上。

Content-Type: multipart/mixed;boundary=batch_AAA1234
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlRBRzN5S3hrZW04eHppQmpmS3RSUFBoQ1liWSJ9.eyJhdWQiOiJodHRwczovL2RlbW8ubHVveW9uZy5tZS8iLCJpc3MiOiJodHRwOi8vc3RzLmx1b3lvbmcubWUvYWRmcy9zZXJ2aWNlcy90cnVzdCIsImlhdCI6MTQ3ODMwNzM5MiwiZXhwIjoxNDc4MzM2MTkyLCJ1cG4iOiJjcm1hZG1pbkBsdW95b25nLm1lIiwicHJpbWFyeXNpZCI6IlMtMS01LTIxLTY1Mzc0MDc5OC0yNDUxMzM4NTMyLTMwMjU3ODY3ODktMTEwNCIsInVuaXF1ZV9uYW1lIjoiTFVPWU9OR1xcY3JtYWRtaW4iLCJhdXRoX3RpbWUiOiIyMDE2LTExLTA1VDAwOjU2OjMyLjMxM1oiLCJhdXRobWV0aG9kIjoidXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQiLCJ2ZXIiOiIxLjAiLCJhcHBpZCI6ImJhMTA2MjY1LWZiM2ItNDllMC1hMGU4LTY4NDBiM2Q3MWFjMiJ9.M8RXfCpBLOoJSazOkmjMIJYyYfYxVGmwojOjO4Pd4Yn9EtbeYK1t0tisP19WTsniHvsRXWaD5bwtLMxQL3DMCOJ1Ftsi7o6gr9U_9URRXffHOqPGOwO5n2Ofxga9pqETi2HPO0PpsyD0hOwMsBkZXCuwjOiYzNdbIY4bq0FDOc4iL7HYTd6xvYAOC8JhNj-H6QuqhrFxRPSrkcKt9Pgkg7T-oblWqZwZEV0sIRbm00pYLSIcPVRiFUmgBQUu7kwqFcwRDgLe7VlSTGlaTRtZphwMB-cYO3LebxDABeMuosZAZPa47g4s8xLpNb5lt9sd365Oebut4jUDhT1VIhNKPw

3)请求体是:

--batch_AAA1234
Content-Type: multipart/mixed;boundary=changeset_BBB4567

--changeset_BBB4567
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 1

POST https://demo.luoyong.me/api/data/v8.1/ly_tests HTTP/1.1
Content-Type: application/json;type=entry

{"ly_name":"批量操作创建的罗勇测试记录","ly_Lookup@odata.bind":"/accounts(CE23165A-3AA3-E511-80C7-000D3A807EC7)","ly_integer":10,"ly_ly_test_ly_testsub_Test":[{'ly_name':'批量操作创建的罗勇测试辅助实体记录1'},{'ly_name':'批量操作创建的罗勇测试辅助实体记录2'}]}

--changeset_BBB4567
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 2

POST https://demo.luoyong.me/api/data/v8.1/tasks HTTP/1.1
Content-Type: application/json;type=entry

{"subject":"批量操作创建的任务","regardingobjectid_account_task@odata.bind":"https://demo.luoyong.me/api/data/v8.1/accounts(C223165A-3AA3-E511-80C7-000D3A807EC7)"}

--changeset_BBB4567
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 3

PATCH $1 HTTP/1.1
Content-Type: application/json;type=entry

{"ly_singlelinetext":"更新了批量操作创建的罗勇测试实体记录"}

--changeset_BBB4567--

--batch_AAA1234--

我帖出一个返回结果,可以看到是普通文本:
使用JS通过Web API执行批量操作,多个操作是一个事务! - 罗勇 - 微软MVP罗勇的博客
 
文本我粘贴放到下面:

"--batchresponse_8071c8bc-f577-44a6-a246-e27c1ec027e2
?Content-Type: multipart/mixed; boundary=changesetresponse_c5387a7e-c5c6-4ea5-95bd-13df85d67046
?
?--changesetresponse_c5387a7e-c5c6-4ea5-95bd-13df85d67046
?Content-Type: application/http
?Content-Transfer-Encoding: binary
?Content-ID: 1
?
?HTTP/1.1 204 No Content
?OData-Version: 4.0
?Location: https://demo.luoyong.me/api/data/v8.1/ly_tests(6c358f7d-54a3-e611-816b-000d3a80c8b8)
?OData-EntityId: https://demo.luoyong.me/api/data/v8.1/ly_tests(6c358f7d-54a3-e611-816b-000d3a80c8b8)
?Access-Control-Expose-Headers: Preference-Applied,OData-EntityId,Location,ETag,OData-Version,Content-Encoding,Transfer-Encoding,Content-Length,Retry-After
?
?
?--changesetresponse_c5387a7e-c5c6-4ea5-95bd-13df85d67046
?Content-Type: application/http
?Content-Transfer-Encoding: binary
?Content-ID: 2
?
?HTTP/1.1 204 No Content
?OData-Version: 4.0
?Location: https://demo.luoyong.me/api/data/v8.1/tasks(6f358f7d-54a3-e611-816b-000d3a80c8b8)
?OData-EntityId: https://demo.luoyong.me/api/data/v8.1/tasks(6f358f7d-54a3-e611-816b-000d3a80c8b8)
?Access-Control-Expose-Headers: Preference-Applied,OData-EntityId,Location,ETag,OData-Version,Content-Encoding,Transfer-Encoding,Content-Length,Retry-After
?
?
?--changesetresponse_c5387a7e-c5c6-4ea5-95bd-13df85d67046
?Content-Type: application/http
?Content-Transfer-Encoding: binary
?Content-ID: 3
?
?HTTP/1.1 204 No Content
?OData-Version: 4.0
?Location: https://demo.luoyong.me/api/data/v8.1/ly_tests(6c358f7d-54a3-e611-816b-000d3a80c8b8)
?OData-EntityId: https://demo.luoyong.me/api/data/v8.1/ly_tests(6c358f7d-54a3-e611-816b-000d3a80c8b8)
?Access-Control-Expose-Headers: Preference-Applied,OData-EntityId,Location,ETag,OData-Version,Content-Encoding,Transfer-Encoding,Content-Length,Retry-After
?
?
?--changesetresponse_c5387a7e-c5c6-4ea5-95bd-13df85d67046--
?--batchresponse_8071c8bc-f577-44a6-a246-e27c1ec027e2--
?"

素格格新疆特产店--做新疆特产的搬运工。
本店由博主的新疆老婆开设,搬运各种上等的新疆特产,是自用,送礼,年货,孝敬长辈特别是(岳)父母的首选!
Dynamics CRM中的地址知多D? - 罗勇 - 微软MVP-罗勇的博客
  评论这张
 
阅读(268)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017