CRM2011发布了用于验证自定义代码的工具(Custom Code Validation Tool)

介绍了Dynamics CRM 2011 R8版本中引入的跨浏览器功能及对JavaScript使用方法的修改,阐述了微软发布的CustomCodeValidationTool的作用,包括其下载链接、验证范围、如何发现问题以及工具特性,同时提供了实例演示和对工具可能存在的局限性的分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于Dynamics CRM 2011 R8(UR9) 中引入了跨浏览器的功能,在实现这项功能的过程中CRM开发团队引入了大量的代码修改,

对于之前的Javascript在CRM中运行的机制,为了向其他主流浏览器进行靠拢,也不得不进行了必要的修改。

比较常见的是,如之前大量使用的htc文件将不再存在(只有IE在使用htc),取而代之的是使用.js文件等;crmForm等在R8之后也不作为跨浏览器的支持范围。


为了方便的验证当前使用的solution是否存在兼容性问题,微软发布了Custom Code Validation Tool用来辅助客户进行代码检查,找出其中的隐患。

下面就是有关这个工具的一些介绍。


工具下载链接:

Microsoft Dynamics CRM 2011 Custom Code Validation Tool


我认为由几个问题作为切入点能更好的描述这个工具的用途以及怎样发挥作用。

我们可以考虑这样几个问题,如果微软对CRM中Javascript的使用方法进行了修改那么哪些地方可能出现问题?会出现什么问题?怎样使用这个工具发现问题?


从第一个问题着手:哪些地方可能出现问题

总结起来主要包含两个方面,按照其影响程度分成红色级别和蓝色级别。

红色级别:当前solution中的Javascript使用了在跨浏览器修改中删除的不被支持的方法。之前许多CRM的方法使用了只被IE支持的技术。

所有受SDK支持的公共方法都被保留了下来,但是,许多并未列入SDK的内部方法和属性已经不再可用并且从此被移除,所以即使IE中也无法再使用这些方法。

这种类型的被标为红色级别,因为以后所有的浏览器中,他们都不会正常工作。

蓝色级别:使用的Javascript只在IE中通过测试并被支持。这类内容有可能在IE之外的其他浏览器中无法正常工作。

如果想要利用CRM 2011的跨浏览器的功能,就必须舍弃crmForm(v4中的用法),转而使用CRM 2011中引入的Xrm.Page对象。


The Custom Code Validation Tool is a CRM managed solution. 

-_-!! Yes, it's testing itself as well..since it has to be imported to the same org you want to test against.


验证范围

将这个solution导入要测试的org,它会检索出所有JavaScriptHTML web resources并将他们列在一个列表中,

选择列表中的项目,相应的内容将会出现在下方的文本框中,如果里面有所谓的红色级别或蓝色级别的内容,

都会相应的标示出来。

还有一点比较好的是,这个工具还会标示出当前方法或属性被使用的次数。影响范围一目了然。

具体的看例子吧。


实例(小邪恶了一把,拿微软自己的ActivityFeeds开刀吧,反正bug那么多就不怕再多找几个错误了,

小小意料之外的是倒也没找出什么标红的方法,只是有几个蓝色的,看了微软自己的solution还是规规矩矩的用SDK里面方法写的)



另外

这个工具也会报假failure哦。所有被注释掉的内容也被列入了统计范围。

小小猜测一下,不会是简单的用字符串来比对,还不处理注释吧,太敷衍有木有,鄙视一下。


引用资料:

1. http://blogs.msdn.com/b/crm/archive/2012/06/21/microsoft-dynamics-crm-2011-custom-code-validation-tool-released.aspx

2. Microsoft Dynamics CRM 2011 Custom Code Validation Tool ReadMe.docx







一阵松香,那是八大关的绿。 ^_^ enjoy it.







/** * Created by RoWe on 2025/04/29 * 创建关联关系接口 */ package com.elite.groovy.callback import com.elite.groovy.util.EliteBean import com.elite.groovy.util.Util import net.sf.json.JSONObject import org.apache.commons.logging.Log import org.apache.commons.logging.LogFactory import org.apache.http.HttpHost import org.apache.http.auth.AuthScope import org.apache.http.auth.UsernamePasswordCredentials import org.apache.http.client.config.RequestConfig import org.apache.http.conn.ssl.NoopHostnameVerifier import org.apache.http.impl.client.BasicCredentialsProvider import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClients import org.apache.http.ssl.SSLContexts import org.apache.http.util.EntityUtils import javax.net.ssl.SSLContext import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse //在工单页面选择手机选项并进行外呼时, CRM系统根据当前工单对应归属机构信息, 传入已经报备好的主叫号调用创建关联关系接口,获取隐私号telX,记录当前创建关联关系信息并将获取的隐私号telX连同调用接口的主叫号一起传给CC平台实现外呼 public class OCNAssociationAdd implements ServiceBackInvoke { private static final Log log = LogFactory.getLog(OCNAssociationAdd.class); otherUtil otherUtil = new otherUtil(); // 代理配置常量 private static final String PROXY_HOST = "wsproxy-dsu.sssit.com" private static final int PROXY_PORT = 8080 private static final String PROXY_USER = "cc-groovy" private static final String PROXY_PASS = "" // 替换为实际密码 public String doCallBack(EliteBean eliteBean, HttpServletRequest request, HttpServletResponse response) { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=UTF-8"); String requestBody = Util.getRequestBody(request, "UTF-8"); String appCode = ""; String appMessage = ""; String dataSource = "dataSource"; JSONObject responseJson = new JSONObject(); JSONObject dataJson = new JSONObject(); CloseableHttpClient httpClient = null; try { JSONObject requestJson = JSONObject.fromObject(requestBody); // 必须每次请求的唯一标识 String requestId = UUID.randomUUID(); List appList = eliteBean.chaxun_sql("SELECT appId,appKey,telA FROM OCNApp WHERE method ='OCNAdd'", dataSource); String appId = appList[0].getAt("appId"); String appKey = appList[0].getAt("appKey"); //主叫号 4008333000 String telA = appList[0].getAt("telA"); //客户号码 String telB = requestJson.getString("telB"); //指定地区 String areaCode = requestJson.getInt("areaCode"); // log.debug("telB",telB); // log.debug("areaCode",areaCode); List AreaInfoList = eliteBean.chaxun_sql("SELECT CCCityCode FROM AreaInfo WHERE areaCode='$areaCode'", dataSource); String CCCityCode = AreaInfoList[0].getAt("CCCityCode"); //是否严格匹配 经过三星老师定版为 0 int areaMatchMode = 0; //创建时间戳 long creTimeStemp = System.currentTimeMillis(); //有效时间 暂定5分钟,单位为毫秒 和后续删除关联关系有关 int expiration = 300000; //long tempStemp=System.currentTimeMillis(); //seqId string(32) 必须绑定关系的序列号 String seqId = ""; //telX string(20) 条件X 号码管理方式为Random 时,系统随机分配的号码 String telX = ""; int InsertOCNAssociation = eliteBean.update( "INSERT INTO OCNAssociation (requestId,telA,telB,areaCode,areaMatchMode,creTimeStemp,expiration) VALUES('${requestId}','${telA}','${telB}','${areaCode}','${areaMatchMode}','${creTimeStemp}','${expiration}') ", dataSource); JSONObject req = new JSONObject(); req.put("requestId", requestId); req.put("telA", telA); req.put("telB", telB); req.put("CCCityCode", CCCityCode); req.put("areaMatchMode", areaMatchMode); req.put("expiration", expiration); OCNRUtils OCNRUtils = new OCNRUtils(); // sm4密钥:将appKey转成md5再取中间16位 String sm4Key = OCNRUtils.get16MD5(appKey); // sm4加密 OCNRUtils.sign(req, appId, appKey); // 设置加密后的member //req.put("member", membersEncrypted); Map headMap = new HashMap(); headMap.put("Content-Type", "application/json"); headMap.put("charset", "utf-8"); List urlSql = eliteBean.chaxun_sql("select url,method from sysc_interface where method ='OCNAdd'", "dataSource"); if (urlSql != null && !urlSql.empty) { // 创建带代理的HTTP客户端 httpClient = createProxyHttpClient(); // 执行POST请求 String webService = "https://ystx.wo.com.cn:19090/privateapi/axb/create"; String responseInfo = doProxyPost(httpClient, webService, req.toString()); // 处理响应 responseJson = JSONObject.fromObject(responseInfo); appCode = responseJson.getString("code"); appMessage = responseJson.getString("msg"); dataJson = responseJson.getJSONObject("data"); responseJson.put('appCode', appCode); responseJson.put('appMessage', appMessage); return responseJson.toString(); } else { appCode = "222"; appMessage = "接口地址维护异常"; } appCode = "222"; appMessage = "测试成功"; responseJson.put('appCode', appCode); responseJson.put('appMessage', appMessage); responseJson.put('requestId', requestId); //responseJson.put('telX', telX); return responseJson; } catch (Exception e) { log.error("e:", e); appCode = "999"; appMessage = "系统异常,请稍后重试"; responseJson.put('appCode', appCode); responseJson.put('appMessage', appMessage); responseJson.put('e', e); return responseJson; } finally { try { if (httpClient != null) httpClient.close(); } catch (Exception e) { log.error("关闭HTTP客户端失败:", e); } } } /** * 创建带代理的HTTP客户端 */ private CloseableHttpClient createProxyHttpClient() { // 代理凭证提供器 def credsProvider = new BasicCredentialsProvider() credsProvider.setCredentials( new AuthScope(PROXY_HOST, PROXY_PORT), new UsernamePasswordCredentials(PROXY_USER, PROXY_PASS) ) // SSL上下文(信任所有证书 - 仅测试环境使用) SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(null, { certs, authType -> true }) .build() // 请求配置 def requestConfig = RequestConfig.custom() .setProxy(new HttpHost(PROXY_HOST, PROXY_PORT)) .setConnectTimeout(30000) // 30秒连接超时 .setSocketTimeout(60000) // 60秒数据传输超时 .build() // 创建HTTP客户端 return HttpClients.custom() .setDefaultCredentialsProvider(credsProvider) .setSSLContext(sslContext) .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .setDefaultRequestConfig(requestConfig) .build() } /** * 通过代理执行POST请求 * @param client HTTP客户端 * @param url 目标URL * @param body 请求体JSON字符串 */ private String doProxyPost(CloseableHttpClient client, String url, String body) { def httpPost = new org.apache.http.client.methods.HttpPost(url) // 设置请求头 httpPost.setHeader("Content-Type", "application/json") httpPost.setHeader("charset", "utf-8") // 设置请求体 httpPost.setEntity(new org.apache.http.entity.StringEntity(body, "UTF-8")) // 执行请求 def response = client.execute(httpPost) def entity = response.getEntity() try { return EntityUtils.toString(entity, "UTF-8") } finally { EntityUtils.consume(entity) } } }代码报错500的原哟
最新发布
07-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值