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 を使用できる場所

Web API を使用して Microsoft Dynamics 365 にアクセスするために、クライアント側 JavaScript を使用できる場所は、2 つあります。

  • JavaScript Web リソース
    HTML の Web リソース、フォーム スクリプト、リボン コマンドのコンテキストで実行される JavaScript Web リソースに含まれる JavaScript コードです。

    Microsoft Dynamics 365 で JavaScript の Web リソースを使用するには、認証の必要はありません。これは Web リソースが、ユーザーが既に認証されているアプリケーションの一部であるためです。 このトピックの後半は、このシナリオを中心に説明します。詳細:Microsoft Dynamics 365 の Web リソーススクリプト (JScript) Web リソースMicrosoft Dynamics 365 での JavaScript の使用、および Microsoft 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 テクノロジを使用した動的な Web ページを提供します。 オブジェクトの名前に「XML」が含まれますが、Web API を使用するすべてのリクエストでは XML ではなく JSON を使用します。

JavaScript フレームワークによって使用されている XMLHttpRequest

jQuery などの JavaScript フレームワークは、基盤とする XMLHttpRequest オブジェクトを関数 ($.ajaxなど) の中にラップします。これは、以前のブラウザーにはネイティブの XMLHttpRequest を標準的な方法で提供しないものがあったため、または簡単に使用できるようにするためです。 現在、モダンなブラウザーでは標準の XMLHttpRequest 実装があるため、これらの相違点を吸収する個別のライブラリは必要ありません。 ただし、多数の開発者はサーバー リソースを要求するために JavaScript フレームワークに依存し続けています。 jQuery や他の JavaScript フレームワークを HTML Web リソースまたは SPA で使用するのは問題ありませんが、フォーム スクリプトおよびリボン コマンドでは使用を避けることをお勧めします。 組織内で利用されるさまざまなソリューションに、それぞれ異なるバージョンの JavaScript フレームワーク が含まれている可能性があり、例えば jQuery では競合回避の手順を全員が実行しない限り、予期しない結果が生じることがあります。 フォーム スクリプトまたはリボン コマンドで Web API リクエストを実行する場合、XMLHttpRequest を直接使用し、jQuery の依存性の影響を受けないようにすることをお勧めします。詳細:jQuery の使用

このトピックでは、ネイティブ XMLHttpRequest を直接使用する方法について説明します。なお、XMLHttpRequest はすべてのブラウザーで使用されているため、jQuery や他の JavaScript フレームワークでも同じ概念が適用できます。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 リソースでは、クライアント側コンテキストでアクセスできる getClientUrl 関数です。この関数は、HTML Web リソースで GetGlobalContext 関数 を使用、またはフォーム スクリプトかリボン コマンドにより Xrm.Page.context オブジェクトを使用してアクセスできます。 サービスに URL を送信するとき、encodeURI 関数を使用して、安全でない文字が入らないようにすることをお勧めします。

この関数はエンティティを作成するため、HTTP リクエスト メソッドは Web API を使用してエンティティを作成する に記載されている通り POST です。

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.parse を使用して XMLHttpRequestresponse プロパティを JavaScript オブジェクトに変換し、message プロパティにアクセスできるようにします。

XMLHttpRequest を送信

最後に、XMLHttpRequestsend メソッドを使用して、必要な JSON データを含むリクエストを送信します。JSON.stringify を使用して JavaScript オブジェクトを JSON 文字列に変換します。これは送信時に、要求の本体に含めることができます。

送信する JSON データの作成

前述の例では、アカウント エンティティは単一のセット プロパティ セットを使用して作成されました。 どのエンティティにどのプロパティが使用できるか判断するには、CSDL メタデータ ドキュメント、その文書から生成された文書、またはその文書から生成されたコードをを見る必要があります。 すべての Microsoft Dynamics 365 の組織に含まれるシステムのビジネス エンティティについては、Web API EntityType Reference を参照してください。 プロパティ名はすべて小文字で、次の JavaScript タイプに対応するシンプルなデータ型を受け付けます: BooleanNumberStringArrayObject、および Date

注意

単純なデータ タイプを使用する場合の唯一の例外は BooleanManagedProperty ComplexType で、Web リソース、テンプレート、レポート、ロール、SavedQueries、メタデータのエンティティなど、ソリューション対応のためのデータを保存するエンティティに使用します。 このプロパティは、ビジネス データを保存するエンティティに使用することはありません。 メタデータ エンティティは、多くの複合型を使用し、別の規則に従います。 詳細については、「Web API を Dynamics 365 メタデータで使用する」を参照してください。

送信するデータを要求内で作成するのは通常、普通の JavaScript オブジェクトの作成と、適切なプロパティの設定を行なう、単純な操作です。 次のコードは、プロパティおよび値のある JavaScript オブジェクトの定義のための、2 つの有効な方法を示しています。 この例では、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 を使用して、コレクション値ナビゲーション プロパティを設定できます。ただし、これは特殊な操作です。詳細:1 回の操作で関連するエンティティを作成する

エンティティ タイプのプロパティ

パラメーターのタイプがエンティティの基本タイプであるアクションにエンティティを送信する場合 (たとえば、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 ライブラリの作成方法を説明します。 ここでは、作成操作で 2 つのパラメーターのみがあります: エンティティ セットの名前、および作成するエンティティの 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 では、Deferred object を経由した CommonJS Promises/A の設計を基にした振舞いが提供されています。ほかには、Promises/A+ 仕様への準拠が主張されています。 これらの実装の相違点については、このトピックでは扱わない範囲です。 このセクションの目的は、単純に Microsoft Dynamics 365 Web API のヘルパー関数がネイティブの XMLHttpRequest オブジェクトを使用しながら、Microsoft Dynamics 365 のモダンなブラウザーで実装されているネイティブの Promise object をどのように記述できるかを紹介します。 次のブラウザーで、ネイティブの確約の実装があります: Google Chrome 32, Opera 19, Mozilla Firefox 29, Apple Safari 8 および Microsoft Edge.

注意

Internet Explorer 11 ではネイティブの確約の実装はありません。 確約の実装がないブラウザー向けに、polyfill を提供する別のライブラリを含む必要があります。 polyfill とは、ブラウザーでネイティブで提供されない機能を実現するコードです。Internet Explorer 11 で確約を使用できるようにする polyfills やライブラリがいくつかあります。es6-promiseq.js、および bluebird です。

確約の使用による恩恵は、次の例で効果的に示されています。 次のコードでは、MyNameSpace.WebAPI.create のコールバックのバージョンが使用され、アカウントを作成し、3 つのタスクを関連付けています。

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

この例の目的では、これらのレコードが、すべてディープ挿入を使用した 1 つの操作で完結するという事実は無視します。詳細:1 回の操作で関連するエンティティを作成する

コールバック コードは、ブロック内のコードで終了するため、難しい場合があります。 一方、確約を使用すると、次のコードで同じレコードを作成できます。

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

確約を使用すると、コードのフローを保持し、単一の catch 関数で起きるエラーをキャッチすることができます。

コールバックを使用した関数を確約を使用するように書き換えるには、コールバック パラメータを削除し、わずかに変更した 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));
});

Promise では、コールバック パラメータを削除するほかに、XMLHttpRequest を追加されます。また、成功コールバックやエラー コールバックに結果やエラーが渡されるかわりに、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. All rights reserved. 著作権