Erste Schritte mit der Microsoft Dynamics 365-Web-API (clientseitiges JavaScript)

 

Veröffentlicht: Januar 2017

Gilt für: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online

In den HTML-Webressourcen, Formularskripten oder Menübandbefehlen können Sie JavaScript verwenden, um Vorgänge für Microsoft Dynamics 365-Daten mithilfe der in Microsoft Dynamics 365 (online und lokal) eingeführten Web API auszuführen.

Die Web API ist besonders einfach mit JavaScript und Webressourcen zu verwenden, da die mit ihr gesendeten und empfangenen JSON-Daten leicht in JavaScript-Objekte konvertiert werden können. Die meisten Entwickler arbeiten jedoch mit einer JavaScript-Hilfsbibliothek zur Codewiederverwendung und zur Trennung der Geschäftslogikcode von Datenzugriffscode. In diesem Thema wird beschrieben, wie Sie das Objekt XMLHttpRequest nutzen, um Vorgänge mit JavaScript auszuführen sowie Möglichkeiten, um wiederverwendbare JavaScript-Bibliotheken zu erstellen, die Funktionen bereitstellen, um mit der Web API zu funktionieren.

In diesem Thema

Wo Sie clientseitiges JavaScript verwenden können

Grundlegendes zu XMLHttpRequest

Verwenden von XmlHttpRequest

Zusammenstellen von JSON-Daten zum Senden

Parsen der zurückgegeben JSON-Daten

Erstellen einer wiederverwendbare Funktion mithilfe von Callbacks

Erstellen einer wiederverwendbaren Funktion mit Promise-Objekten

Wo Sie clientseitiges JavaScript verwenden können

Es gibt zwei Bereiche, in denen Sie clientseitiges JavaScript verwenden können, um auf Microsoft Dynamics 365 mithilfe der Web-API zuzugreifen:

  • JavaScript Webressourcen
    JavaScript-Code, der in einer JavaScript-Webressource enthalten ist, die im Kontext einer HTML-Webressource ausgeführt wird, Formularscripts oder Menübandbefehle.

    Wenn Sie JavaScript-Webressourcen in Microsoft Dynamics 365 verwenden, müssen Sie sich nicht authentifizieren, da die Webressourcen Teil der Anwendung sind, bei der der Benutzer bereits authentifiziert ist. Der Rest dieses Hilfethemas konzentriert sich auf dieses Szenario.Weitere Informationen:Webressourcen für Microsoft Dynamics 365,Webressourcen für Skripts (JScript), Verwendung von JavaScript mit Microsoft Dynamics 365 und JavaScript-Bibliotheken für Microsoft Dynamics 365.

  • Einzelseitenanwendungen
    JavaScript-Code in einer JavaScript-Bibliothek von einer anderen Anwendung, die in einem Browser ausgeführt wird und sich bei Microsoft Dynamics 365 mithilfe der Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing; CORS) authentifiziert. Dieses Muster wird normalerweise für Einzelseitenanwendungen verwendet.

    Wenn Sie JavaScript in einer Einzelseitenanwendung (Single Page Application; SPA) verwenden, können Sie die adal.js-Bibliothek verwenden, um dem Benutzer zu erlauben, sich zu authentifizieren und auf Microsoft Dynamics 365-Daten auf einer Seite, die auf einer anderen Domäne gehostet wird, zuzugreifen. Die meisten der Informationen in diesem Thema gelten für dieses Szenario, doch Sie müssen einen Autorisierungsheader in jede Anforderung integrieren, die ein Authentifizierungstoken enthält. Weitere Informationen finden Sie unter Verwenden von OAuth mit Cross-Origin Resource Sharing, um eine Single Page-Anwendung mit Microsoft Dynamics 365 zu verbinden.

Grundlegendes zu XMLHttpRequest

Wenn Sie die Web API verwenden, wird sie ein XMLHttpRequest-Objekt verwenden.XMLHttpRequest (XHR) ist ein Objekt, das in allen modernen Browsern vorhanden ist. Es ermöglicht AJAX-Techniken für dynamische Webseiten . Obwohl der Name des Objekts "XLM" enthält, verwenden alle Anfragen über die Web-API JSON statt XML.

XMLHttpRequest über JavaScript-Frameworks

JavaScript-Frameworks wie jQuery packen das zugrunde liegende XMLHttpRequest-Objekt häufig in eine Funktion (wie z. B. $.ajax), da nicht alle Browser eine natives XMLHttpRequest bieten, und um die Verwendung zu vereinfachen. Nachdem moderne Browser über eine standardisierte XMLHttpRequest-Implementierung verfügen, benötigen Sie keine separate Bibliothek, um diese Unterschiede zu kompensieren. Entwickler nutzen dennoch JavaScript-Frameworks, um Serverressourcen anzufordern. Während es in Ordnung ist, jQuery und andere JavaScript-Frameworks in HTML-Webressourcen oder SPAs zu nutzen, empfehlen wir jedoch, diese in Formularskripten oder in Menübandbefehlen zu meiden. Viele unterschiedliche Lösungen mit möglicherweise unterschiedlichen Versionen eines JavaScript-Frameworks (insbesondere jQuery) können unerwartete Ergebnisse nach sich ziehen wenn nicht entsprechende Schritte zur Vermeidung von Konflikten ausgeführt werden. Wenn Sie Web-API-Anfragen in Formularskripten oder in Menübandbefehlen nutzen, wird empfohlen, das Sie XMLHttpRequest direkt nutzen statt von jQuery abhängig zu sein.Weitere Informationen:Verwendung von jQuery

In diesem Thema wird beschrieben, wie mithilfe XMLHttpRequest direkt verwendet, die Konzepte gelten jedoch auch für jQuery oder andere JavaScript-Frameworks im Browser, da sie alle XMLHttpRequest verwenden. Sie können eine Bibliothek, die XHR verwendet, die direkt in einem Browser mit jedem JavaScript-Framework verwendet.

Verwenden von XmlHttpRequest

Das folgende Beispiel stellt ein einfaches Beispiel zur Erstellung einer Firmaenentität über die Web-API mithilfe des XMLHttpRequest-Objekts dar. In diesem Beispiel wird nur die clientURL-Variable nicht definiert.

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

In den folgenden Abschnitten wird beschrieben, was dieser Code macht.

Öffnen von XMLHttpRequest

Nachdem Sie das XMLHttpRequest-Objekt initialisiert haben, müssen Sie es öffnen, bevor Sie die Eigenschaften festlegen oder es senden können. Die open-Methodenparameter sind eine HTTP-Anforderungsmethode, ein URL-Parameter und ein boolean-Parameter, der angibt, ob der Vorgang asynchron ausgeführt werden soll. Sie sollten den Vorgang immer asynchron auszuführen.Weitere Informationen:Verwenden Sie asynchrone Datenzugriffsmethoden.

In diesem Beispiel erstellen wir eine Firmaenentität. Daher müssen wir die für den URL Entitätspfad für account EntityType festlegen. Die vollständige URL im vorliegenden Beispiel ist clientURL + "/api/data/v8.1/accounts und die clientURL-Variable muss auf Stamm-URL der Microsoft Dynamics 365-Anwendung festgelegt werden. Für Webressourcen, die Zugriff auf das Kontextobjekt haben, kann auf die getClientUrl-Funktion über das verfügbare clientseitige Kontextobjekt entweder mithilfe der GetGlobalContext-Funktion-Funktion in einer HTML-Webressource oder dem Xrm.Page.context-Kontextobjekt in einem Formularskript oder Menübandbefehl zugegriffen werden. Sie sollten die Funktion encodeURI verwenden auf jede URL anwenden, die Sie senden. So stellen Sie sicher, dass sie keine unsicheren Zeichen enthält.

Da diese Funktion eine Entität erstellt, ist die HTTP-Anforderungsmethode wie unter Erstellen einer Entität mithilfe des Web-API beschrieben POST.

Die XMLHttpRequestopen-Methode stellt kann außerdem mit einem Benutzernamen und Kennwort arbeiten. Sie müssen keinen Wert für diese Parameter mit Webressourcen angeben, da der Benutzer bereits authentifiziert ist. Für die SPAs wird die Authentifizierung durch einen Token anstelle dieser Parameter verwaltet.

Festlegen der Header und der Ereignishandler

Nach dem Öffnen des XMLHttpRequest können Sie mehrere Anforderungsheader mithilfe der setRequestHeader-Methode anwenden. Sie sollten die hier gezeigten Header mit einigen Variationen für bestimmte Arten von Vorgängen verwenden.Weitere Informationen:HTTP-Kopfzeilen.

Bevor Sie die Anfrage senden, müssen Sie einen der Ereignishandler integrieren, der erkennt, wenn der Vorgang abgeschlossen ist. Nach dem Senden, durchläuft die Anfrage verschiedene Statuswerte, bevor die Antwort zurückgegeben wird. Um zu erkennen das XMLHttpRequest abgeschlossen wurde, müssen Sie der Eigenschaft onreadystatechange einen Ereignishandler zuweisen, der erkennt, wenn Eigenschaft readystate den Wert 4 hat (abgeschlossen). Zum diesem Zeitpunkt kann die Eigenschaft status überprüft werden.

Hinweis

Nach Abschluss von XMLHttpRequest ist eine bewährte Methode die onreadystatechange-Eigenschaft auf null festzulegen, um mögliche Speicherverlustprobleme zu vermeiden.

Innerhalb der anonymen Funktion Ihres Ereignishandlers, den Sie den Abschluss überprüft haben, können Sie die status-Eigenschaft überprüfen, um zu bestimmen, ob der Vorgang erfolgreich war. In diesem Fall ist der Statuswert 204 No Content, da im Textteil der Antwort kein Inhalt erwartet wird. Die URI für die erstellte Firma befindet sich im OData-EntityId-Header und kann mithilfe der Methode getResponseHeader abgerufen werden.

Wenn dies ein anderer Vorgang wäre, bei in der Antwort dem Daten zurückzugeben werden sollten, würde sie einen 200 OKstatus-Wert haben und die Funktion würde JSON.parse in der XMLHttpRequest-Antwort verwenden, um die JSON-Antwort in ein JavaScript-Objekt zu konvertieren, auf das Ihr Code zugreifen kann.Weitere Informationen:Parsen der zurückgegeben JSON-Daten

Wenn der Status nicht den erwarteten Wert hat, wird ein Fehlerobjekt mit den in Analysefehler von der Antwort beschriebenen Eigenschaften zurückgegeben. Dieses Beispiel verwendet JSON.parse, um die XMLHttpRequestresponse-Eigenschaft in ein JavaScriptObjekt zu konvertieren, damit auf die message-Eigenschaft zugegriffen werden kann.

Senden von XMLHttpRequest

Schließlich verwenden Sie die Methode XMLHttpRequestsend, um die Anforderung zu senden, einschließlich alle benötigten JSON-Daten. Verwenden Sie JSON.stringify, um JavaScript-Objekte in JSON-Zeichenfolgen zu konvertieren, die in den Text der Anforderung beim Senden einbezogenen werden können.

Zusammenstellen von JSON-Daten zum Senden

Im vorangehenden Beispiel wird die Firmenentität nur mithilfe eines einzelnen Eigenschaftensatzes erstellt. Um die Eigenschaften für eine Entität die verfügbar sind zu bestimmen, müssen Sie im CSDL-Metadatendokument-Dokument, in der von diesem Dokument erstellten Dokumentation oder in mithilfe dieses Dokuments generiertem Code nachsehen. Für Systemgeschäftsentitäten, die in allen Microsoft Dynamics 365-Organisationen enthalten sind, können Sie auf die Web API EntityType Reference verweisen. Eigenschaftennamen werden klein geschrieben und nehmen einfache Datentypen entgegen, die den folgenden JavaScript-Typen entsprechen: Boolean, Number, String, Array, Object und Date.

Hinweis

Die einzige Ausnahme zur Verwendung einfacher Datentypen ist der BooleanManagedProperty ComplexType, der für Entitäten verwendet wird, die lösungsbewusste Daten wie Webressourcen, Vorlagen, Berichte, Rollen, gespeicherte Anfragen und in Metadatenentitäten speichern. Diese Eigenschaft wird nie für Entitäten verwendet, in denen Geschäftsdaten gespeichert werden. Metadatenentitäten verwenden viele komplexe Typen und folgen verschiedenen Regeln. Weitere Informationen finden Sie unter Verwenden der Web-API mit Dynamics 365-Metadaten.

Das Zusammenstellen von Daten für eine Anforderung wird normalerweise einfache durch das Erstellen eines gewöhnlichen JavaScript-Objektes und des Festlegen von entsprechenden Eigenschaften durchgeführt. Der folgende Code enthält zwei gültige Methoden zum Definieren eines JavaScript-Objekts mit Eigenschaften und Werten. In diesem Beispiel werden ausgewählte Eigenschaften der Kontaktentität verwendet, die in contact EntityType definiert wird.

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

Unabhängig davon, wie diese Objekte definiert werden, werden Sie nach der Verwendung von JSON.stringify in dieselbe JSON-Zeichenfolge konvertiert.

    {
     "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)"
    }

Es ist möglich, dass Sie eine Eigenschaft festlegen müssen, die nicht den normalen Eigenschaften-Benennungsrichtlinien für JavaScript folgt. Wenn Sie z. B. den Wert einer Navigationseigenschaft mit einem einzelnen Wert beim Erstellen einer Entität festlegen, müssten Sie @odata.bind an den Namen der Eigenschaft anhängen und der Wert auf eine URL für die entsprechende Entität festlegen. In diesem Fall müssen Sie die Eigenschaft in einem Stil mit eckigen Klammern definieren (wie im vorherigen Beispiel zu sehen).

Wenn Sie nicht mit Metadatenentitäten arbeiten, legen Sie keine Entitätseigenschaften für ein Objekt fest. Bei Metadatenentitäten müssen Sie häufig Eigenschaften festlegen, die komplexer sind oder Enumerationswerte darstellen. Dies ist jedoch bei normalen Geschäftsentitäten nicht der Fall.

Wenn Sie verknüpfte Entitäten erstellen, legen Sie den Wert Navigationseigenschaft mit einem Collection-Wert über ein Array fest. Dies ist jedoch ein eher ungewöhnlicher Vorgang.Weitere Informationen:Erstellen verknüpfter Entitäten in einem Vorgang

Entitätstypeigenschaften

Wenn Sie eine Entität für eine Aktion nutzen, bei der der Parametertyp ein Basistyp der Entität ist, beispielsweise crmbaseentity EntityType oder activitypointer EntityType, müssen Sie möglicherweise die Eigenschaft @odata.type mit dem vollständigen Namen des Entitätstyps als Wert einbeziehen. Beispielsweise erbt letter EntityType von activitypointer. Daher müssen Sie den Typ der Entität möglicherweise mithilfe der folgende Eigenschaft und den Wert explizit angeben: "@odata.type": "Microsoft.Dynamics.CRM.letter".

Senden von Daten für Aktualisierungsvorgänge

Wenn Sie Entitäten aktualisieren, ist es wichtig, Eigenschaftswerte nur für Eigenschaften festzulegen, die Sie aktualisieren möchten. Sie sollten eine Entität abrufen, Eigenschaften der abgerufenen Instanz ändern und diese Instanz dann in einem Aktualisierungsvorgang verwenden. Stattdessen sollten Sie ein neues Objekt erstellt neuen und Eigenschaften für die zu aktualisierenden Eigenschaften festlegen.

Wenn Sie diese einfach alle Eigenschaften einer abgerufenen Entität kopiert und mit PATCH aktualisieren, werden alle Eigenschaften, die Sie senden, als ein Update angehen (auch, wenn der Wert mit dem aktuellen Wert identisch ist). Wenn die Überwachung für die Entität und das Attribut aktiviert ist, werden die Daten als geändert angezeigt, obwohl tatsächlich keine Änderungen stattgefunden hat.Weitere Informationen:Grundlegende Aktualisierung

Parsen der zurückgegeben JSON-Daten

Obwohl der Erstellungsvorgang im vorangehenden Beispiel keine JSON-Daten zurückgegeben hat, geben die meisten Vorgänge mit GETJSON-Daten zurück. Für die meisten Datentypen, die zurückgegeben werden, kann JSON mit den folgenden Codezeile in JavaScript konvertieren werden.

var data = JSON.parse(this.response)

Allerdings können Daten, die Datumsangaben enthält, ein Problem verursacht werden, da Datumswerte als Zeichenfolge übergeben werden (z. B. 2015-10-25T17:23:55Z). Um diese in eine JavaScriptDate-Objekt zu konvertieren, müssen Sie den Parameter reviver für die Funktion JSON.parse verwenden. Im Folgenden finden Sie ein Beispiel einer Funktion, die verwendet werden kann, um Datumsangaben zu parsen.

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

Diese Funktion übernehmen Sie einfach als Parameter, wie hier zu sehen.

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

Erstellen einer wiederverwendbare Funktion mithilfe von Callbacks

Wenn Sie den Code für einen bestimmten Vorgang haben, möglichen Sie diesen erneut verwenden statt ihn immer wieder neu zu schreiben. Die nächste Schritt ist, eine JavaScript-Bibliothek zu erstellen, die eine Funktion enthält, um den Vorgang mit allen verfügbaren Optionen auszuführen. In diesem Fall gibt es lediglich zwei Variablen für den Erstellungsvorgang: den Entitätsnamen und die JSON-Definition der Entität. Statt den ganzen zuvor gezeigten Code zu schreiben kann der Vorgang einer Funktion zusammengefasst werden, der mit ein paar Codezeilen verwendet werden kann.

Asynchrone Vorgänge mit JavaScript verfügen über Callback-Funktionen. Diese dienen dazu, Rückgabewerte aus dem asynchronen Vorgang zu sammeln und die Logik im Programm fortzusetzen. Mithilfe des zuvor beschrieben Codes für den Erstellungsvorgang ist es hier das Ziel, denselben Vorgang mit dem folgenden Code auszuführen.

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

In diesem Beispiel stellt MyNameSpace.WebAPI die bewährte Methode zur Angabe eine eindeutigen Namens für alle verwendeten Funktionen dar.Weitere Informationen:Definieren Sie eindeutige Namen für Ihre JavaScript-Funktionen.

Für diese Bibliothek planen wir Funktion für zusätzliche Vorgänge. So können Sie wiederverwendbare Funktionen für unterstützende Vorgänge nutzen. Der folgende Code zeigt eine Bibliothek, die dies demonstriert und eine MyNameSpace.WebAPI.create-Funktion mit Callbacks umfasst.

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

Diese Bibliothek zeigt die bewährte Methode des Definierens einer Funktion in einer selbstausführenden anonymen Funktion und des Einfügens der Funktion in den MyNameSpace.WebAPI-Namespace. Dies ermöglicht die Definition interner Funktionen, die nicht vom anderem Code zugreifbar sind. Sämtliche Funktionen, mit die als Teil des this-Schlüsselwortes definiert sind, sind öffentlich. Alle Funktionen innerhalb der anonymen Funktion können über öffentlichen Funktionen verwendet werden, jedoch nicht von externem Code außerhalb der anonymen Funktion. Der Code innerhalb der Funktion kann nicht von anderem Code auf der Seite geändert werden.

Der Namespace ist so definiert, dass er keinen anderen Code überschreibt, der den gleichen Namespace verwendet. Er überschreibt jedoch alle Funktionen mit demselben Namen, die Teil dieses Namespace sind. Sie können die separate Bibliotheken erstellen, die zusätzliche öffentliche Funktionen zum Namespace hinzufügen, sofern diese nicht den gleichen Namen besitzen.

Die MyNameSpace.WebAPI.create-Funktion bietet die folgenden Parameter:

Name

Beschreibung

entitySetName

Der Entitätsname für den Entitätstyp, den Sie erstellen möchten.

entity

Ein Objekt mit den Eigenschaften für die Entität, die Sie erstellen möchten.

successCallback

Die Funktion, die nach dem Erstellen der Entität aufgerufen wird. Der URI der erstellten Entitäten wird an diese Funktion übergeben.

errorCallback

Die Funktion, die bei einem Fehler aufgerufen wird. Der Fehler wird an diese Funktion übergeben.

Der Code, der das XMLHttpRequest-Objekt konfiguriert, wurde geändert, um diese Parameterwerte und eine zusätzliche interne Hilfsfunktion getWebAPIPath zu nutzen, den die URI der Basisorganisation sucht und die URL anhängt, um die Stamm-URI für die Web-API bereitzustellen. Sie müssen sie daher nicht hinzuzufügen. Die URI für die erstellte Entität wird an successCallback übergeben, falls diese definiert ist. Entsprechend wird die öffentlich Funktion errorHandler verwendet, um jeden Fehler zu analysieren, der zurückgegeben wird. Die Funktion errorHandler muss öffentlich sein, da sie innerhalb des Ereignishandlers für das Ereignis onreadystatechange aufgerufen wird und sich nicht im Kontext des Namespace befindet. Sie muss unter Verwendung des vollständigen Namens aufgerufen werden: MyNameSpace.WebAPI.errorHandler.

Erstellen einer wiederverwendbaren Funktion mit Promise-Objekten

Callbacks werden normalerweise für asynchrone Vorgänge verwendet. Viele Entwickler nehmen Sie als recht schwerfällig und schlecht lesbar und zu debuggen wahr, da auf eine asynchrone Vorgänge aufeinander aufbauen, um Code erstellen, der eine "pyramid of doom" erstellt da der Code bei anonymen Funktionen mehr und mehr nach rechts eingerückt wird. Obwohl dieses Problems über benannte Funktionen anstelle von anonymen Funktionen behoben werden kann, schätzen viele Entwickler die Vorteile von Promise-Objekten. Ein Promise-Objekt stellt einen Vorgang dar, der noch nicht abgeschlossen ist, jedoch zukünftig abgeschlossen wird.

Es gibt zahlreiche Drittanbieterbibliotheken und JavaScript-Frameworks, mit verschiedenen Implementierungen von Promise-Objekten.JQuery bietet ein Verhalten auf Basis des CommonJS Promises/A-Design über das Deferred-Objekt und andere arbeiten mit der Promises/A+-Spezifikation. Eine Erläuterung der Unterschiede zwischen diesen Implementierungen liegt außerhalb des Rahmens dieses Themas. Dieser Abschnitt dient dazu eine Hilfsfunktion für die Microsoft Dynamics 365-Web-API über das Objekt XMLHttpRequest zu erstellen, die das native Promise-Objekt verwendet, dass in den meisten modernen Browsern von Microsoft Dynamics 365unterstützt wird. Die folgenden Browsern bieten eine systemeigene Implementierung von Promises: Google Chrome 32, Opera 19, Mozilla Firefox 29, Apple Safari 8 und Microsoft Edge.

Hinweis

Internet Explorer 11 implementiert keine nativen Zusagen. Für Webbrowser, die nicht Promises nicht implementieren, müssen Sie eine separate Bibliothek einbinden, um ein polyfill bereitzustellen. Ein Polyfill ist Code, der Funktionen bereitstellt, die von einem Browser nicht nativ bereitgestellt werden. Es gibt mehrere Polyfills oder Bibliotheken, die es Internet Explorer 11 ermöglichen, Zusagen zu haben: es6-promise, q.js und bluebird.

Der Vorteil der Verwendung von Promises können am Besten durch ein Beispiel gezeigt werden. Der folgende Code verwendet die Callback-Version von MyNameSpace.WebAPI.create, um eine Firma zu erstellen und anschließend drei Aufgaben zuzuordnen.

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

Ignorieren in diesem Beispiel, dass alle Datensätze in einem einzelnen Vorgang über "deep insert" erstellt werden könnten.Weitere Informationen:Erstellen verknüpfter Entitäten in einem Vorgang

Der Callback-Code ist schwierig, da er im in der Mitte des Codeblocks endet. Mit Promises können Sie die gleichen Datensätze mit dem folgenden Code erstellen.

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

Mit Promises bleibt der Code-Fluss erhalten und Sie können alle Fehler Abfangen mit einer einzigen Catch-Funktion abfangen.

Die Funktion mit Callbacks für Promise-Objekte zu konvertieren bedeutet nur, die Callback-Parameter zu Entfernen und ein leicht geändertes XMLHttpRequest-Objekte zurückzugeben (siehe folgendes Codebeispiel).

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

Neben dem Entfernen Callback-Parameter wird das XMLHttpRequest-Objekt in das Promise-Objekt einbezogen. Statt die Ergebnisse oder Fehler an die entsprechenden Callbacks zurückzugeben werden Sie an resolve- oder reject-Parameter übergeben. Der folgende Code zeigt die gesamte JavaScript-Bibliothek mit der MyNameSpace.WebAPI.create-Funktion. Sie müssen nur noch weitere wiederverwendbarere Web API-Vorgänge hinzufügen.

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

Siehe auch

Verwenden der Microsoft Dynamics 365-Web-API
Verwenden von Dynamics 365-Daten mithilfe von Webressourcen
Vorgänge mithilfe der Web-API ausführen
Web API Beispiele (Clientseitiges JavaScript)
Verwenden von OAuth mit Cross-Origin Resource Sharing, um eine Single Page-Anwendung mit Microsoft Dynamics 365 zu verbinden

Microsoft Dynamics 365

© 2017 Microsoft. Alle Rechte vorbehalten. Copyright