分享方式:


開始使用 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 資料執行作業,使用 Microsoft Dynamics 365 (線上和內部部署) 中導入的 Web API。

Web API 特別容易搭配 JavaScript 和 Web 資源使用,因為透過它傳送和接收的 JSON 資料可輕鬆轉換成 JavaScript 物件。 即使因此,大部分開發人員仍想要建立或使用 Helper JavaScript 程式庫,以便重複使用程式碼,並將其商務邏輯程式碼與存取資料的程式碼分開。 本主題說明如何使用 XMLHttpRequest 物件搭配 JavaScript 執行作業,以及建立可重複使用的 JavaScript 程式庫的機會,以便提供可搭配 Web API 使用的函數。

本主題內容

可以使用用戶端 JavaScript 的位置

了解 XMLHttpRequest

使用 XMLHttpRequest

撰寫要傳送的 JSON 資料

剖析傳回的 JSON

使用回呼建立可重複使用的函數

使用 Promise 建立可重複使用的函數

可以使用用戶端 JavaScript 的位置

可以使用 Web API 用戶端 JavaScript 存取 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 資源或 SPA 中使用 jQuery 和其他 JavaScript 架構,但是我們建議避免在表單指令碼或功能區命令中使用它們。 組織內安裝了各種解決方案之後,每一種都可能包含不同版本的 JavaScript 架構,尤其是 jQuery,因此可能造成未預期的結果,除非每個人都採取步驟避免發生衝突。 如果您將在表單指令碼或功能區命令中執行 Web API 要求,建議您直接使用 XMLHttpRequest,並不要倚賴 jQuery。其他資訊:b7840a25-f25e-409b-8b6a-0ef04c7ed9c4#BKMK_UsingjQuery

本主題說明如何直接使用原生 XMLHttpRequest,不過相同的概念適用於使用在瀏覽器中執行的 jQuery 或其他 JavaScript 架構,因為這些全都使用 XMLHttpRequest。 您可以使用直接在瀏覽器中使用 XHR 的程式庫搭配任何 JavaScript 架構。

使用 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 參數,用以指出作業是否應非同步執行。 您應一律選擇非同步執行作業。其他資訊:b7840a25-f25e-409b-8b6a-0ef04c7ed9c4#bkmk_useasynchronous

在此範例中,由於我們要建立帳戶實體,因此需要設定 URL,使其符合 account EntityType 的實體集路徑。 此範例中的完整 URL 為 clientURL + "/api/data/v8.1/accounts,而且 clientURL 變數必須設為 Microsoft Dynamics 365 應用程式的根 URL。 對於能夠存取內容物件的 Web 資源,可透過用戶端內容物件存取的 getClientUrl 函數,藉由使用 HTML Web 資源中的 GetGlobalContext 函數 或表單指令碼或功能區命令中的 Xrm.Page.context 物件。 您應該使用 encodeURI 函數,在您傳送至服務的任何 URL 上,以確保它不會包含任何不安全的字元。

由於此函數會建立實體,因此 HTTP 要求方法是 POST,如使用 Web API,建立實體中所述。

XMLHttpRequestopen 方法也可供指定使用者名稱和密碼。 您不需要為這些含有 Web 資源的參數指定值,因為使用者已經過驗證。 對於 SPA,驗證是透過權杖管理,而不是這些參數。

設定標題和事件處理常式

在開啟 XMLHttpRequest 後,您可以使用 setRequestHeader 方法套用一些要求標題。 通常您應該使用這裡顯示的標題,並針對某些特殊作業進行變化。其他資訊:HTTP 標頭

在傳送要求之前,必須包括事件處理常式,以便偵測作業何時完成。 在傳送要求之後,它會進行幾種狀態轉變,然後才傳回回覆。 若要擷取 XMLHttpRequest 完成的時刻,您必須將事件處理常式設定為 onreadystatechange 屬性,以偵測 readystate 屬性何時等於 4,表示完成。 此時您可以檢查 status 屬性。

備註

XMLHttpRequest 完成後,最好將 onreadystatechange 屬性設定為 null,避免發生可能的記憶體流失問題。

在屬於事件處理常式的匿名函數中,確認完成後,就可以檢查 status 屬性判斷作業是否成功。 在此情況下,預期的狀態值是 204 No Content,因為建立作業的回應本文中不會有任何內容。 所建立帳戶的 URI 位於 OData-EntityId 標題中,可以使用 getResponseHeader 方法存取。

如果這是不同的作業,預期會在回覆中傳回資料,則會有 200 OKstatus 值,而且函數會在 XMLHttpRequest 回覆上使用 JSON.parse 將 JSON 回覆轉換成您的程式碼可以存取的 JavaScript 物件。其他資訊:剖析傳回的 JSON

如果狀態不是預期的值,表示錯誤,且錯誤物件會隨從回覆剖析錯誤中所述的屬性傳回。 此範例使用 JSON.parseXMLHttpRequestresponse 屬性轉換成 JavaScript 物件,以便存取 message 屬性。

傳送 XMLHttpRequest

最後,使用 XMLHttpRequestsend 方式傳送要求,包括需要的任何 JSON 資料。 使用 JSON.stringify 將 JavaScript 物件轉換成 JSON 字串,可在傳送時包含在要求的本文中。

撰寫要傳送的 JSON 資料

在前述範例中,帳戶實體只使用單一屬性集建立。 若要判斷哪些屬性可供實體使用,您需要查看 CSDL 中繼資料文件,從該文件產生的說明文件,或使用該文件產生的程式碼。 對於所有 Microsoft Dynamics 365 組織中包括的系統商務實體,您可以參考 Web API EntityType Reference。 屬性名稱為小寫,可接受符合下列 JavaScript 類型的簡易資料類型:BooleanNumberStringArrayObjectDate

備註

使用簡單資料類型的唯一例外是 BooleanManagedProperty ComplexType,它用於儲存解決方案感知資料的實體,例如 Web 資源、範本、報告、角色、已儲存查詢以及中繼資料實體。 此屬性絕不會用於儲存商務資料的實體。 中繼資料實體使用許多複雜的類型,並遵循不同的規則。 如需詳細資訊,請參閱使用 Web API 搭配 Dynamics 365 中繼資料

撰寫要在要求中傳送的資料通常很簡單,只要建立一般 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 代表為您使用的任何函數提供唯一名稱的最佳做法。其他資訊:b7840a25-f25e-409b-8b6a-0ef04c7ed9c4#bkmk_DefineUniqueNames

對於這個程式庫,我們打算包含額外作業的函數,以便有機會重複使用私用函數支援作業。 下列程式碼顯示的程式庫示範此操作,且包括使用回呼的 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 物件的程式碼已經過修改,會使用這些參數值以及額外的內部 Helper 函數 getWebAPIPath,它會尋找基底組織 URI 並附加 URL,以符合 Web API 的根 URI,因此您不需要包括它。 所建立實體的 URI 會傳遞至 successCallback (如已定義)。 同樣地,公用 errorHandler 函數會用來剖析任何傳回的錯誤。errorHandler 函數必須是公用的,因為它會在 onreadystatechange 事件的事件處理常式內呼叫,而且這不是在命名空間的範圍內。 它必須使用全名呼叫:MyNameSpace.WebAPI.errorHandler

使用 Promise 建立可重複使用的函數

雖然傳統上非同步作業是使用回呼,但是許多開發人員覺得它們有些不靈光,而且難以讀取和偵錯,因為一系列非同步作業會以彼此為基礎建置,以建立程式碼而形成「無盡模式」(Pyramid of doom),因為縮排造成使用匿名函數的程式碼一直往頁面最右邊移動。 雖然此問題可藉由使用具名函數解決,而不使用匿名函數,但是許多開發人員仍感謝「Promise」提供的優點。Promise 物件代表尚未完成,但預期在未來完成的作業。

有許多協力廠商程式庫和 JavaScript 架構提供不同的 Promise 實作。JQuery 根據 CommonJS Promises/A 設計提供了一種行為,透過順延物件,而其他則堅持遵循 Promises/A+ 規格。 這些實作之間的差異則不在本主題的說明範圍之內。 本節的目的僅在於說明如何針對使用原生 XMLHttpRequest 物件的 Microsoft Dynamics 365 Web API 撰寫 Helper 函數,以使用在 Microsoft Dynamics 365 所支援的大部分現代化瀏覽器中實作的原生 Promise 物件。 下列瀏覽器有原生的 Promise 實作:Google Chrome 32、Opera 19、Mozilla Firefox 29、Apple Safari 8 和 Microsoft Edge。

備註

Internet Explorer 11 不實作原生 Promise。 對於不實作原生 Promise 的瀏覽器,您必須包含不同的程式庫來提供 polyfill。 polyfill 是程式碼,可提供瀏覽器原本未提供的功能。 有幾個 polyfill 或程式庫可讓 Internet Explorer 11 擁有 Promise:es6-promiseq.jsbluebird

使用 Promise 的好處可透過範例做最佳示範。 下列程式碼使用回呼版本的 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); });

此範例會忽略這些記錄全都可以在單一作業中建立,使用深層插入。其他資訊:在單一作業中建立相關實體

回呼程式碼具有挑戰性,因為它在程式碼區塊中間結束。 而使用 Promise 可以建立相同的記錄,使用下列程式碼。

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); });

使用 Promise 可保留程式碼的流暢性,同時可在單一擷取函數中抓出任何發生的錯誤。

將使用回呼的函數轉換成使用 Promise,就是將回呼參數移除並傳回稍微修改過的 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. 著作權所有,並保留一切權利。 著作權