Microsoft Dynamics 365 Web API(客户端 JavaScript)入门

 

发布日期: 2017年1月

适用于: Dynamics 365 (online),Dynamics 365 (on-premises),Dynamics CRM 2016,Dynamics CRM Online

在 HTML Web 资源、窗体脚本或功能区命令中,可以使用 JavaScript,以便使用 Microsoft Dynamics 365(在线或本地) 中引入的 Web API 对 Microsoft Dynamics 365 数据执行操作。

Web API 在与 JavaScript 和 Web 资源配合使用时特别简单,因为通过它收发的 JSON 数据可以轻松转换为 JavaScript 对象。 就算这样,大多数开发人员仍然希望创建或使用帮助程序 JavaScript 库来重复利用代码和将业务逻辑代码与他们的代码分开,以便访问数据。 此主题介绍如何使用 XMLHttpRequest 对象和 JavaScript 执行操作,以及创建可重复利用且提供用于 Web API 的函数的 JavaScript 库的机会。

本主题内容

哪里可以使用客户端 JavaScript

了解 XMLHttpRequest

使用 XMLHttpRequest

撰写要发送的 JSON 数据

解析返回的 JSON

使用回调创建可重用的函数

使用承诺创建可重用的函数

哪里可以使用客户端 JavaScript

有两个区域可以使用客户端 JavaScript 访问使用 Web API 的 Microsoft Dynamics 365:

  • JavaScript Web 资源
    包含在 JavaScript Web 资源中的运行在 HTML Web 资源、窗体脚本或功能区命令中的 JavaScript 代码。

    在 Microsoft Dynamics 365 中使用 JavaScript Web 资源时无需进行身份验证,因为 Web 资源是已经对用户进行身份验证的应用程序的一部分。 本主题的其余部分将主要介绍此情形。详细信息:Microsoft Dynamics 365 的 Web 资源脚本 (JScript) Web 资源通过 Microsoft Dynamics 365 使用 JavaScriptMicrosoft Dynamics 365 的 JavaScript 库

  • 单个页面应用程序
    在浏览器中运行并使用跨源资源共享 (CORS) 验证到 Microsoft Dynamics 365 的另一个应用程序的 JavaScript 库中的 JavaScript 代码。 此模式通常用于单个页面应用程序。

    当您在单个页面应用程序 (SPA) 中使用 JavaScript 时,您可以使用 adal.js 库允许用户验证和访问位于其他域中托管的页面的 Microsoft Dynamics 365 数据。 本主题的大部分信息适用于此情形,但您还必须将一个授权标题集成到包含一个身份验证令牌的请求中。 有关详细信息,请参阅使用 OAuth 和跨源资源共享将单页应用程序连接到 Microsoft Dynamics 365

了解 XMLHttpRequest

当您使用 Web API 时将使用一个 XMLHttpRequest 对象。XMLHttpRequest (XHR) 是所有现代浏览器中都可以找到的一种本机对象,它使 AJAX 技术让网页动态化。 虽然该对象的名称中包含“XML”,但使用 Web API 的所有请求都使用 JSON,而不是使用 XML。

JavaScript 框架使用的 XMLHttpRequest

JavaScript 框架 (如 jQuery)通常将基础 XMLHttpRequest 对象包装为函数(如 $.ajax),因为以前并非所有浏览器都以标准方式提供本机 XMLHttpRequest,并且还简化了使用。 鉴于现代浏览器拥有标准的 XMLHttpRequest 实施,所以您不需要单独的库来缓和这些差异。 不过许多开发人员仍然依赖 JavaScript 框架来请求服务器资源。 虽然可以在 HTML Web 资源中使用 jQuery 与其他 JavaScript 框架,我们还是建议避免在窗体脚本或功能区命令中使用它们。 由于一个组织可能安装多种解决方案,并且每个解决方案可能包含 JavaScript 框架的不同版本,特别是 jQuery,所以除非所有人都采取措施避免冲突,否则可能会产生意外结果。 如果您将通过窗体脚本或功能区命令执行 Web API 请求,建议您直接使用 XMLHttpRequest,不要依赖 jQuery。详细信息:jQuery 的使用

本主题介绍如何直接使用本机 XMLHttpRequest,但是同样的概念在使用 jQuery 或在浏览器中运行的 JavaScript 框架时也适用,因为它们全都使用 XMLHttpRequest。 可以在采用任何 JavaScript 框架的浏览器中使用直接采用 XHR 的库。

使用 XMLHttpRequest

下面是一个非常简单的示例,该示例显示如何使用 Web API 和 XMLHttpRequest 对象创建客户实体。 在此示例中,只有 clientURL 变量未定义。

var req = new XMLHttpRequest()
req.open("POST",encodeURI(clientURL + "/api/data/v8.1/accounts"), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
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 == 204) {
   var accountUri = this.getResponseHeader("OData-EntityId");
   console.log("Created account with URI: "+ accountUri)
  }
  else {
   var error = JSON.parse(this.response).error;
   console.log(error.message);
  }
 }
};
req.send(JSON.stringify({ name: "Sample account" }));

以下各节介绍了此代码的用途。

打开 XMLHttpRequest

初始化 XMLHttpRequest 对象后,必须先将其打开,然后才能设置属性或发送该对象。open 方法参数是 HTTP 请求方法、URL 和 boolean 参数,用于指示是否应异步执行操作。 应始终选择异步执行操作。详细信息:使用异步数据访问方法

在此示例中,因为我们将创建客户实体,所以需要设置 URL 来与 account EntityType 的实体集路径匹配。 此示例中的完整 URL 是 clientURL + "/api/data/v8.1/accounts,并且 clientURL 变量必须设置为 Microsoft Dynamics 365 应用程序的根 URL。 对于具有上下文对象访问权限的 Web 资源,可以通过使用 HTML Web 资源中的 GetGlobalContext 函数 可用的客户端上下文对象或窗体脚本或功能区命令中的 Xrm.Page.context 对象访问的 getClientUrl 函数。 应该对您发送到服务的任何 URL 使用 encodeURI 函数,以确保其中不包含不安全的字符。

因为此函数创建实体,所以 HTTP 请求方法是 使用 Web API 创建实体 中介绍的 POST。

还提供 XMLHttpRequestopen 方法,用于指定用户名和密码。 无需使用 Web 资源为这些参数指定一个值,因为已经对用户进行身份验证。 对于 SPA,通过令牌而不是这些参数管理身份验证。

设置标头和事件处理程序

在打开 XMLHttpRequest 之后,可以使用 setRequestHeader 方法应用一些请求标头。 通常应该对特殊类型的操作使用此处显示的标头和一些变体。详细信息:HTTP 标头

在发送请求之前,需要添加一个事件处理程序,用于检测操作何时完成。 在发送该请求后,返回响应之前,它将经历若干状态。 若要捕获 XMLHttpRequest 的结束时间,必须为 onreadystatechange 属性设置事件处理程序来检测 readystate 属性何时等于 4,4 表示完成。 此时您可以检查 status 属性。

备注

XMLHttpRequest 完成后,最好将 onreadystatechange 属性设置为 null,以避免潜在的内存泄漏问题。

在匿名函(即您的事件处理程序)中,您已验证完成后,可以检查 status 属性以确定操作是否成功。 在这种情况下,预期的状态值为 204 No Content,因为创建操作的响应正文中不应该有任何内容。 创建的客户的 URI 位于 OData-EntityId 标头中,可以使用 getResponseHeader 方法访问。

如果这是本该在响应中返回数据的其他操作,则具有 200 OK个status 值,并且该函数在 XMLHttpRequest 中使用 JSON.parse 将 JSON 转换为您的代码可以访问的 JavaScript 对象。详细信息:解析返回的 JSON

如果状态不是预期值,则为错误,并且使用解析响应错误中介绍的属性返回错误对象。 此示例使用 JSON.parseXMLHttpRequestresponse 属性转换为 JavaScript 对象,以便可以访问 message 属性。

发送 XMLHttpRequest

最后,请使用 XMLHttpRequestsend 方法发送请求,包括需要的所有 JSON 数据。 使用 JSON.stringify 将 JSON 对象转换为发送请求时请求的正文中可包含的 JavaScript 字符串。

撰写要发送的 JSON 数据

在前面的示例中,客户实体是仅使用一个属性集创建的。 要确定哪些属性可用于实体,您需要查看从该文档生成的的 CSDL 元数据文档 文档,或使用该文档生成的代码。 对于所有 Microsoft Dynamics 365 组织中包含的系统业务实体,可以参阅 Web API EntityType Reference。 属性名为小写,并接受与以下 JavaScript 类型对应的简单数据类型:BooleanNumberStringArrayObjectDate

备注

有关如何使用简单的数据类型的唯一的例外是使用为实体存储可识别解决方案的数据(如 Web 资源,模板,报表,角色,savedqueries)和元数据实体中的 BooleanManagedProperty ComplexType。 该属性不会用于存储业务数据的实体。 元数据实体使用许多复杂类型并遵循不同的规则。 有关详细信息,请参阅使用具有 Dynamics 365 元数据的 Web API

撰写要在请求中发送的数据通常不过是创建普通的 JavaScript 对象和设置适当的属性。 以下代码显示两种使用属性和值定义 JavaScript 对象的方法。 该示例使用从 contact EntityType 中定义的联系人实体选择的属性。

var contact = new Object();
contact.firstname = "John";
contact.lastname = "Smith";
contact.accountrolecode = 2; //Employee
contact.creditonhold = false; //Number value works here too. 0 is false and 1 is true
contact.birthdate = new Date(1980, 11, 2);
contact["parentcustomerid_account@odata.bind"] = "/accounts(f3a11f36-cd9b-47c1-8c44-e65b961257ed)"

var contact = {
 firstname: "John",
 lastname: "Smith",
 accountrolecode: 2,//Employee
 creditonhold: false,
 birthdate: new Date(1980, 11, 2),
 "parentcustomerid_account@odata.bind": "/accounts(f3a11f36-cd9b-47c1-8c44-e65b961257ed)"
};

无论这些对象是如何定义的,在您使用 JSON.stringify 之后,它们都将转换为同一个 JSON 字符串。

{
     "firstname": "John",
     "lastname": "Smith",
     "accountrolecode": 2,
     "creditonhold": false,
     "birthdate": "1980-12-02T08:00:00.000Z",
     "parentcustomerid_account@odata.bind": "/accounts(f3a11f36-cd9b-47c1-8c44-e65b961257ed)"
}

有时您必须定义不遵守 JavaScript 的普通属性命名规则的属性。 例如,创建实体时设置单值导航属性的值期间,您需要向属性名追加 @odata.bind,并将该值设置为域相关实体对应的 URL。 在这种情况下,必须以上一个示例中显示的括号批注样式定义该属性。

除了处理元数据实体时,其他时间不需要将实体属性设置为对象。 对于元数据实体,经常需要设置属于复杂类型或枚举值的属性。 但这对普通业务实体则不常见。

创建相关实体时,可以使用 Array 设置集合值导航属性的值,但此操作专业性相当高。详细信息:在一个操作中创建相关实体

实体类型属性

当您将实体发布到参数类型表示实体的基类(如 crmbaseentity EntityTypeactivitypointer EntityType)的操作时,您可能需要添加具有实体类型全名的 **@odata.type** 属性充当该值。 例如,因为 letter EntityType 继承自 activitypointer,您可能需要使用以下属性和值明确指定实体的类型:"@odata.type": "Microsoft.Dynamics.CRM.letter"。

为更新操作发送数据

当您更新实体时,只能为您要更新的属性设置属性值。 您不应检索实体,更新检索到的实例的属性,然后在更新操作中使用该实例。 相反,您应创建一个新的对象,并仅为您要更新的属性设置新属性。

如果只是复制检索到的实体的所有属性并使用 PATCH 更新该实体,将把您发送的每个属性视为一个更新,即使值与当前值相同也不例外。 如果您对实体和属性启用了审核,将在值中实际无更改时指示数据已更改。详细信息:基本更新

解析返回的 JSON

虽然前面的示例中使用的创建操作不返回 JSON 数据,但是大部分使用 GET 的操作将返回 JSON。 对于返回的大多数数据类型,可以使用下面的代码行将 JSON 转换为 JavaScript。

var data = JSON.parse(this.response)

但是,包含日期的数据是一个问题,因为日期作为字符串(如 2015-10-25T17:23:55Z)传递。 若要将此转换为 JavaScriptDate 对象,必须对 JSON.parse 函数使用 reviver 参数。 下面是可用于解析日期的函数的示例。

function dateReviver(key, value) {
  var a;
  if (typeof value === 'string') {
   a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
   if (a) {
    return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
   }
  }
  return value;
 };

若要应用此函数,只需将其作为参数添加,如下所示。

var data = JSON.parse(this.response,dateReviver)

使用回调创建可重用的函数

当您拥有了用于执行特定操作的代码时,您会希望重复使用该代码,而不是一再编写相同代码。 下一步是创建一个包含和任何可用选项一起执行操作的函数的 JavaScript 库。 在这种情况下,创建操作只有两个变量:实体集名称和要创建的实体的 JSON 定义。 无需编写前面显示的所有代码,而是可以在只需要几行代码即可使用的函数中包含相同操作。

与 JavaScript 之间的异步操作传统上采用回调函数,用于捕获异步操作的任何返回值,并继续您的程序中的罗。 通过使用上面介绍的创建操作的代码,此处的目标是实施仅使用以下代码执行同一操作。

MyNameSpace.WebAPI.create("accounts",
{ name: "Sample account" },
function (accountUri) { console.log("Created account with URI: " + accountUri) },
function (error) { console.log(error.message); });

在此示例中,MyNameSpace.WebAPI 提供为您使用的任何函数提供唯一名称的最佳实践。详细信息:为 JavaScript 函数定义唯一名称

对于此库,我们计划纳入针对更多操作的函数,以便有机会让可重用的专用函数为操作提供支持。 以下代码显示可以表明这一点的库,并包含使用回调的 MyNameSpace.WebAPI.create 函数。

"use strict";
var MyNameSpace = window.MyNameSpace || {};
MyNameSpace.WebAPI = MyNameSpace.WebAPI || {};
(function () {
 this.create = function (entitySetName, entity, successCallback, errorCallback) {
  var req = new XMLHttpRequest();
  req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
  req.setRequestHeader("Accept", "application/json");
  req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
  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 == 204) {
     if (successCallback)
      successCallback(this.getResponseHeader("OData-EntityId"));
    }
    else {
     if (errorCallback)
      errorCallback(MyNameSpace.WebAPI.errorHandler(this.response));
    }
   }
  };
  req.send(JSON.stringify(entity));
 };

 //Internal supporting functions
 function getClientUrl() {
  //Get the organization URL
  if (typeof GetGlobalContext == "function" &&
      typeof GetGlobalContext().getClientUrl == "function") {
   return GetGlobalContext().getClientUrl();
  }
  else {
   //If GetGlobalContext is not defined check for Xrm.Page.context;
   if (typeof Xrm != "undefined" &&
       typeof Xrm.Page != "undefined" &&
       typeof Xrm.Page.context != "undefined" &&
       typeof Xrm.Page.context.getClientUrl == "function") {
    try {
     return Xrm.Page.context.getClientUrl();
    } catch (e) {
     throw new Error("Xrm.Page.context.getClientUrl is not available.");
    }
   }
   else { throw new Error("Context is not available."); }
  }
 }
 function getWebAPIPath() {
  return getClientUrl() + "/api/data/v8.1/";
 }

 // This function is called when an error callback parses the JSON response
 // It is a public function because the error callback occurs within the onreadystatechange 
 // event handler and an internal function would not be in scope.
 this.errorHandler = function (resp) {
  try {
   return JSON.parse(resp).error;
  } catch (e) {
   return new Error("Unexpected Error")
  }
 }

}).call(MyNameSpace.WebAPI);

该库演示在自执行匿名函数(也称为自调用匿名函数或立即调用匿名函数)内定义函数和将该函数附加到 MyNameSpace.WebAPI 命名空间的最佳实践。 这允许您定义其他代码无法访问的内部函数。 作为 this 组成部分定义的任何函数都是公用函数,而匿名函数内的所有函数都可以被公用函数使用,但是不能被匿名函数外部的代码使用。 函数内的代码可以被该页中的其他代码修改。

定义此命名空间是为了不覆盖使用相同命名空间的其他任何代码,但是将覆盖该命名空间中的所有同名函数。 您可以创建单独的库,用于向命名空间添加更多公用函数,前提是它们不同名。

MyNameSpace.WebAPI.create 函数提供以下参数:

客户

说明

entitySetName

您要创建的实体的类型的实体集名称。

entity

拥有您要创建的实体的属性的对象。

successCallback

创建实体时要调用的函数。 将把创建的实体的 Uri 传递到该函数。

errorCallback

出错时要调用的函数。 错误将传递到该函数。

用于配置 XMLHttpRequest 对象的代码已修改为使用这些参数值,以及另外一个内部帮助程序函数 getWebAPIPath,该函数用于查找基础组织 URI 和追加 URL 以匹配 Web API 的根 URI,这样就不需要包括它。 将把创建的实体的 URL 传递到 successCallback(如果已定义)。 同样,errorHandler 公用函数也用于解析返回的所有错误。errorHandler 函数必须为公用函数,因为它在 onreadystatechange 事件的事件处理程序内调用,而不是在命名空间的范围内调用。 必须使用全名 MyNameSpace.WebAPI.errorHandler 调用该函数。

使用承诺创建可重用的函数

虽然回调传统上用于异步操作,但是许多开发人员觉得回调有点不好用,难以读取和调试,因为一系列异步操作基于彼此构建来创建“金字塔穹顶”形式的代码,原因是在使用异步函数时,索引会导致代码向页面右侧移动得越来越厉害。 虽然此问题可以通过使用命名函数而不是匿名函数来解决,但是许多开发人员喜欢“承诺”的优点。Promise 对象表示尚未完成,但有望在将来完成的操作。

许多第三方库和 JavaScript 框架提供不同的承诺实施。JQuery 已通过延迟的对象提供了基于 CommonJS Promises/A 设计的行为,而其他的则坚持遵守 Promises/A+ 规范。 这些实施之间的区别的说明超出了本文的范畴。 本节的目标只是介绍如何为 Microsoft Dynamics 365 Web API 编写使用本机 XMLHttpRequest 对象的帮助程序函数,以便使用在 Microsoft Dynamics 365 支持的大多数现代浏览器中实施的本机承诺对象。 以下浏览器拥有对承诺的本机实施:Google Chrome 32、Opera 19、Mozilla Firefox 29、Apple Safari 8 和 Microsoft Edge。

备注

Internet Explorer 11 不实施本机承诺。 对于不实施本机承诺的浏览器,您必须包括一个单独的库来提供 polyfill。 polyfill 是提供浏览器不本机提供的功能的代码。 有若干个 polyfill 或库将允许 Internet Explorer 11 具有承诺:es6-promiseq.jsbluebird

使用承诺的优点是最适合通过示例演示。 以下代码使用 MyNameSpace.WebAPI.create 的回调版本创建客户,然后为其关联三个任务。

MyNameSpace.WebAPI.create("accounts",
 { name: "Sample account" },
 function (accountUri) {
  console.log("Created account with URI: " + accountUri);
  MyNameSpace.WebAPI.create("tasks",
   { subject: "Task 1", "regardingobjectid_account_task@odata.bind": accountUri },
   function () {
    MyNameSpace.WebAPI.create("tasks",
     { subject: "Task 2", "regardingobjectid_account_task@odata.bind": accountUri },
     function () {
      MyNameSpace.WebAPI.create("tasks",
       { subject: "Task 3", "regardingobjectid_account_task@odata.bind": accountUri },
       function () {
        //Finished creating three tasks
        console.log("Three tasks created");
       },
      function (error) { console.log(error.message); });
     },
     function (error) { console.log(error.message); });
   },
  function (error) { console.log(error.message); });
 },
function (error) { console.log(error.message); });

为了达到此示例的目的,请忽略以下事实:可以使用深度插入通过一个操作创建所有这些记录。详细信息:在一个操作中创建相关实体

该回调代码非常具有挑战性,因为它在代码块的中间结束。 同时,您可以使用承诺和下面的代码创建相同的记录。

var accountUri;
MyNameSpace.WebAPI.create("accounts", { name: "Sample account" })
.then(function (aUri) {
 accountUri = aUri;
 console.log("Created account with URI: " + accountUri);
})
.then(function () {
 return MyNameSpace.WebAPI.create("tasks", { subject: "Task 1", "regardingobjectid_account_task@odata.bind": accountUri });
})
.then(function () {
 return MyNameSpace.WebAPI.create("tasks", { subject: "Task 2", "regardingobjectid_account_task@odata.bind": accountUri });
})
.then(function () {
 return MyNameSpace.WebAPI.create("tasks", { subject: "Task 3", "regardingobjectid_account_task@odata.bind": accountUri });
})
.catch(function (error) { console.log(error.message); });

使用代码可以保持代码流,并可以通过单个捕获函数捕获出现的任何错误。

将具有回调的函数转换为使用承诺不过是删除回调参数并返回稍加修改的 XMLHttpRequest,如以下代码示例中所示。

return new Promise(function (resolve, reject) {
 var req = new XMLHttpRequest();
 req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
 req.setRequestHeader("Accept", "application/json");
 req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
 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 == 204) {
  resolve(req.getResponseHeader("OData-EntityId"));
  }
  else {
  reject(MyNameSpace.WebAPI.errorHandler(req.response));
  }
 }
 };
 req.send(JSON.stringify(entity));
});

除了删除回调参数外,XMLHttpRequest 也包含在 Promise 中,而不是将结果或错误传递到成功或错误回调,它们将传递到 resolvereject 参数。 以下代码表示包含 MyNameSpace.WebAPI.create 函数的整个 JavaScript 库。 接下来只需要使用相同的模式添加更多可重用的 Web API 操作。

"use strict";
var MyNameSpace = window.MyNameSpace || {};
MyNameSpace.WebAPI = MyNameSpace.WebAPI || {};
(function () {
 /** @description Create a new entity
  * @param {string} entitySetName The name of the entity set for the type of entity you want to create.
  * @param {object} entity An object with the properties for the entity you want to create.
  */
 this.create = function (entitySetName, entity) {
  /// <summary>Create a new entity</summary>
  /// <param name="entitySetName" type="String">The name of the entity set for the entity you want to create.</param>
  /// <param name="entity" type="Object">An object with the properties for the entity you want to create.</param>       
  if (!isString(entitySetName)) {
   throw new Error("MyNameSpace.WebAPI.create entitySetName parameter must be a string.");
  }
  if (isNullOrUndefined(entity)) {
   throw new Error("MyNameSpace.WebAPI.create entity parameter must not be null or undefined.");
  }

  return new Promise(function (resolve, reject) {
   var req = new XMLHttpRequest();
   req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
   req.setRequestHeader("Accept", "application/json");
   req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
   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 == 204) {
      resolve(req.getResponseHeader("OData-EntityId"));
     }
     else {
      reject(MyNameSpace.WebAPI.errorHandler(req.response));
     }
    }
   };
   req.send(JSON.stringify(entity));
  });

 };

 //Internal supporting functions
 function getClientUrl() {
  //Get the organization URL
  if (typeof GetGlobalContext == "function" &&
      typeof GetGlobalContext().getClientUrl == "function") {
   return GetGlobalContext().getClientUrl();
  }
  else {
   //If GetGlobalContext is not defined check for Xrm.Page.context;
   if (typeof Xrm != "undefined" &&
       typeof Xrm.Page != "undefined" &&
       typeof Xrm.Page.context != "undefined" &&
       typeof Xrm.Page.context.getClientUrl == "function") {
    try {
     return Xrm.Page.context.getClientUrl();
    } catch (e) {
     throw new Error("Xrm.Page.context.getClientUrl is not available.");
    }
   }
   else { throw new Error("Context is not available."); }
  }
 }
 function getWebAPIPath() {
  return getClientUrl() + "/api/data/v8.1/";
 }

 //Internal validation functions
 function isString(obj) {
  if (typeof obj === "string") {
   return true;
  }
  return false;

 }
 function isNull(obj) {
  if (obj === null)
  { return true; }
  return false;
 }
 function isUndefined(obj) {
  if (typeof obj === "undefined") {
   return true;
  }
  return false;
 }
 function isFunction(obj) {
  if (typeof obj === "function") {
   return true;
  }
  return false;
 }
 function isNullOrUndefined(obj) {
  if (isNull(obj) || isUndefined(obj)) {
   return true;
  }
  return false;
 }
 function isFunctionOrNull(obj) {
  if (isNull(obj))
  { return true; }
  if (isFunction(obj))
  { return true; }
  return false;
 }

 // This function is called when an error callback parses the JSON response.
 // It is a public function because the error callback occurs in the onreadystatechange 
 // event handler and an internal function wouldn’t be in scope.
 this.errorHandler = function (resp) {
  try {
   return JSON.parse(resp).error;
  } catch (e) {
   return new Error("Unexpected Error")
  }
 }

}).call(MyNameSpace.WebAPI);

另请参阅

使用 Microsoft Dynamics 365 Web API
使用 Web 资源处理 Dynamics 365 数据
使用 Web API 执行操作
Web API 示例(客户端 JavaScript)
使用 OAuth 和跨源资源共享将单页应用程序连接到 Microsoft Dynamics 365

Microsoft Dynamics 365

© 2017 Microsoft。 保留所有权利。 版权