Aan de slag met de web-API van Microsoft Dynamics 365 (JavaScript op de client)
Gepubliceerd: januari 2017
Is van toepassing op: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online
In HTML-webresources, formulierscripts of lintopdrachten kunt u JavaScript gebruiken om bewerkingen uit te voeren op Microsoft Dynamics 365 gegevens door middel van de Web API die is geïntroduceerd in Microsoft Dynamics 365 (online en on-premises).
De Web API is vooral simpel te gebruiken met JavaScript en webresources, omdat de JSON-gegevens die ermee worden verzonden en ontvangen gemakkelijk worden geconverteerd naar JavaScript-objecten. Toch is het voor de meeste ontwikkelaars aan te raden om een helper-JavaScript-bibliotheek te maken of te gebruiken zodat ze code kunnen hergebruiken en hun code voor bedrijfslogica gescheiden te houden van de code waarmee toegang tot gegevens wordt verkregen. In dit onderwerp wordt beschreven hoe u het XMLHttpRequest-object kunt gebruiken om bewerkingen met JavaScript uit te voeren, evenals mogelijkheden om herbruikbare JavaScript-bibliotheken te maken die functies bevatten om te werken met de Web API.
In dit onderwerp
Waar kunt u JavaScript op de client gebruiken
XMLHttpRequest
XMLHttpRequest gebruiken
Te verzenden JSON-gegevens samenstellen
Geretourneerde JSON parseren
Een herbruikbare functie maken met callbacks
Een herbruikbare functie maken met promises
Waar kunt u JavaScript op de client gebruiken
Er zijn twee gebieden waar u aan de clientzijde JavaScript kunt gebruiken voor toegang tot Microsoft Dynamics 365 met de web-API :
JavaScript-webresources.
JavaScript code die is opgenomen in een JavaScript-webresource die wordt uitgevoerd in de context van een HTML-webresource, formulierscripts of lintopdrachten.Wanneer u webresources van JavaScript gebruikt in Microsoft Dynamics 365 hoeft u niet te verifiëren, omdat de webresources deel uitmaken van de toepassing waarvoor de gebruiker al is geverifieerd. De rest van dit onderwerp gaat verder in op dit scenario.Meer informatie:Webresources voor Microsoft Dynamics 365,Scriptwebresources (JScript), JavaScript gebruiken met Microsoft Dynamics 365 en JavaScript-bibliotheken voor Microsoft Dynamics 365.
Toepassingen met één pagina
JavaScript-code in een JavaScript-bibliotheek uit een andere toepassing, die wordt uitgevoerd in een browser en wordt geverifieerd bij Microsoft Dynamics 365 met Cross-Origin Resource Sharing (CORS). Dit patroon wordt meestal gebruikt in toepassingen met één pagina.Als u JavaScript gebruikt in toepassing met één pagina (Single Page Application, SPA), kunt u de bibliotheek adal.js gebruiken om de gebruiker toe te staan zich te verifiëren en toegang te geven tot Microsoft Dynamics 365-gegevens die worden gehost in een ander domein. Het grootste deel van de informatie in dit onderwerp geldt voor dit scenario, maar u moet ook een autorisatiekoptekst integreren in elke aanvraag die een verificatietoken bevat. Voor meer informatie, zie Een toepassing met één pagina door middel van OAuth met Cross-Origin Resource Sharing verbinding laten maken met Microsoft Dynamics 365
XMLHttpRequest
Als u de Web API gebruikt, gebruikt X een XMLHttpRequest-object.XMLHttpRequest (XHR) is een native object dat wordt aangetroffen in alle moderne browsers, en AJAX-technieken mogelijk maakt om webpagina's dynamisch te maken. Hoewel de naam van het object de aanduiding "XML" bevat, maken alle aanvragen die de Web API gebruiken eerder gebruik van JSON dan van XML.
XMLHttpRequest gebruikt door JavaScript-frameworks
JavaScript-frameworks zoals jQuery nemen het onderliggende XMLHttpRequest-object op in een functie (zoals $.ajax), omdat voorheen niet alle browsers een native XMLHttpRequest op een standaardmanier aanboden, maar ook omdat dit gemakkelijker was. Nu de moderne browsers een standaardimplementatie van XMLHttpRequest bevatten, hebt u geen afzonderlijke bibliotheek nodig om deze verschillen te beperken. Toch blijven veel ontwikkelaars gebruik maken van JavaScript-frameworks om de serverbronnen op te vragen. Het is weliswaar geen probleem om jQuery en andere JavaScript-frameworks te gebruiken in HTML-webresources of SPA's, maar het wordt afgeraden om deze toe te passen in formulierscripts of lintopdrachten. Aangezien bij een organisatie verschillende oplossingen kunnen zijn geïnstalleerd, die elk mogelijk een andere versie van een JavaScript-framework bevatten, met name jQuery, kan dit leiden tot onverwachte resultaten tenzij iedereen maatregelen neemt om conflicten te voorkomen. Als u Web API-aanvragen uitvoert in formulierscripts of lintopdrachten, wordt het aanbevolen om rechtstreeks gebruik te maken van XMLHttpRequest en geen afhankelijkheid van jQuery in te bouwen.Meer informatie:jQuery gebruiken
In dit onderwerp wordt beschreven hoe u native XMLHttpRequest rechtstreeks kunt toepassen. Dezelfde concepten gelden wanneer u gebruik maakt van jQuery of ander JavaScript-frameworks die in een browser worden uitgevoerd, aangezien deze allemaal XMLHttpRequest gebruiken. U kunt in een browser met een willekeurig JavaScript-framework een bibliotheek toepassen die rechtstreeks XHR gebruikt.
XMLHttpRequest gebruiken
Het volgende is een zeer eenvoudig voorbeeld van de manier waarop u een accountentiteit kunt maken, door middel van de Web API en het XMLHttpRequest-object. In dit voorbeeld is alleen de variabele clientURL niet gedefinieerd.
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" }));
De volgende secties beschrijven wat deze code doet.
XMLHttpRequest openen
Nadat u het object XMLHttpRequest hebt geïnitialiseerd, moet u het openen voordat u eigenschappen kunt instellen of het kunt verzenden. De parameters voor de methode open zijn een HTTP-aanvraagmethode , een, URL en een boolean-parameter om aan te geven of de bewerking asynchroon moet worden uitgevoerd. Het wordt aanbevolen om bewerkingen altijd asynchroon te laten uitvoeren.Meer informatie:Gebruik asynchrone gegevenstoegangsmethoden
Omdat hier een accountentiteit wordt aangemaakt, moet in dit voorbeeld de URL worden ingesteld overeenkomstig de entiteitenreekspad voor hetaccount EntityType. De volledige URL in dit voorbeeld is clientURL + "/api/data/v8.1/accounts en de variabele clientURL moet zijn ingesteld op de basis-URL van de Microsoft Dynamics 365-toepassing. Voor webresources die toegang hebben tot het contextobject, kan de functie getClientUrl die toegankelijk is via het client-contextobject dat beschikbaar is via ofwel de Functie GetGlobalContext in een HTML-webresource of via het object Xrm.Page.context in een formulierscript of lintopdracht. Gebruik de functie encodeURI voor elke URL die u naar de service verzendt, om ervoor te zorgen dat deze geen onveilige tekens bevat.
Omdat deze functie een entiteit maakt, is de HTTP-aanvraagmethode POST zoals beschreven in Een entiteit maken met de web-API.
De methode XMLHttpRequestopen biedt ook mogelijkheid voor het opgeven van een gebruikersnaam en een wachtwoord. U hoeft geen waarde voor deze parameters op te geven voor webresources, omdat de gebruiker al is geverifeerd. Voor SPA's wordt de verificatie beheerd door een token in plaats van door deze parameters.
De headers en gebeurtenisregistratie instellen
Nadat u de XMLHttpRequest hebt geopend, kunt u een aantal aanvraag-headers toepassen door middel van de methode setRequestHeader. Het wordt aanbevolen om de headers te gebruiken die hier worden besproken, met eventuele aanpassingen voor speciale bewerkingen.Meer informatie:HTTP-headers.
Voordat u de aanvraag verzendt, moet u een gebeurtenisregistratie opnemen die detecteert wanneer de bewerking is voltooid. Nadat u de aanvraag hebt verzonden, doorloopt deze meerdere statussen voordat de respons wordt geretourneerd. Als u wilt vastleggen wanneer XMLHttpRequest is voltooid, moet u een gebeurtenisregistratie instellen op de eigenschap onreadystatechange, om vast te stellen wanneer de eigenschap readystate gelijk is aan 4, wat betekent dat de bewerking is voltooid. Op dat moment kunt u de eigenschap status onderzoeken.
Notitie
Nadat de XMLHttpRequest is voltooid, is het een best practice om de eigenschap onreadystatechange in te stellen op null om mogelijke problemen met geheugenlekken te voorkomen.
Nadat voltooiing is vastgesteld kunt u in de anonieme functie die uw gebeurtenisregistratie is de eigenschap status onderzoeken om te bepalen of de bewerking is geslaagd. In dit geval is de verwachte statuswaarde 204 No Content omdat niets wordt verwacht in de hoofdtekst van de respons die een maakbewerking retourneert. De URI voor de gemaakte account bevindt zich in de OData-EntityId-header en kan worden gelezen door middel van de methode getResponseHeader.
Als dit een andere bewerking was, waarvan de respons gegevens zou moeten bevatten, zou dit de waarde 200 OKstatus hebben en de functie zou JSON.parse gebruiken op de XMLHttpRequest-respons om de JSON-respons om te zetten naar een JavaScript-object dat uw code zou kunnen lezen.Meer informatie:Geretourneerde JSON parseren
Als de status niet de verwachte waarde heeft, is er een fout opgetreden en wordt een foutobject geretourneerd met de eigenschappen die worden beschreven in Parseerfouten van de respons. In dit voorbeeld wordt JSON.parse gebruikt om de eigenschap XMLHttpRequestresponse om te zetten naar een JavaScript-object zodat de eigenschap message kan worden gelezen.
XMLHttpRequest verzenden
Ten slotte gebruikt u de methode XMLHttpRequestsend om de aanvraag te verzenden, inclusief alle vereiste JSON-gegevens. Gebruik JSON.stringify om JavaScript-objecten om te zetten naar JSON-tekenreeksen die bij verzending in de tekst van de aanvraag kunnen worden opgenomen.
Te verzenden JSON-gegevens samenstellen
In het vorige voorbeeld wordt de accountentiteit gemaakt met gebruik van een enkele reeks eigenschappen. Om te bepalen welke eigenschappen beschikbaar zijn voor een entiteit moet u het CSDL-metagegevensdocument bekijken, of documentatie die vanuit dat document is gegenereerd, of code die is gegenereerd door middel van dat document. Voor systeembedrijfsentiteiten die zijn opgenomen in alle Microsoft Dynamics 365-organisaties, kunt u de Web API EntityType Reference raadplegen. Eigenschapsnamen worden geschreven in kleine letters en accepteren eenvoudige gegevenstypen die overeenkomen met de volgende JavaScript-typen: Boolean, Number, String, Array, Object en Date
Notitie
De enige uitzondering op het gebruik van eenvoudige gegevenstypen is de BooleanManagedProperty ComplexType, die wordt gebruikt voor entiteiten die oplossingsondersteunende gegevens opslaan, zoals webresources, sjablonen, rapporten, rollen en opgeslagen query's, en in metagegevensentiteiten. Deze eigenschap wordt nooit gebruikt voor entiteiten waarin zakelijke gegevens worden opgeslagen. Metagegevensentiteiten gebruiken een groot aantal complexe typen en volgen andere regels. Raadpleeg De web-API gebruiken met Dynamics 365-metagegevens voor meer informatie.
Het opstellen van gegevens om met een aanvraag te verzenden omvat doorgaans alleen het maken van een normaal JavaScript-object en het instellen van de juiste eigenschappen. De volgende code laat twee geldige methoden zien om een JavaScript-object te definiëren met eigenschappen en waarden. In dit voorbeeld worden geselecteerde eigenschappen gebruikt van de contactpersoon-entiteit die in contact EntityType is gedefinieerd.
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)"
};
Ongeacht hoe deze objecten worden gedefinieerd, worden ze na toepassing van JSON.stringify beide geconverteerd naar dezelfde JSON-tekenreeks.
{
"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)"
}
Soms moet u een eigenschap definiëren die niet de normale richtlijnen voor naamgeving van eigenschappen voor JavaScript volgt. Wanneer u bijvoorbeeld de waarde van een single-valued navigatie-eigenschap instelt bij het maken van een entiteit, moet u @odata.bind toevoegen aan de naam van de eigenschap en de waarde instellen op een URL die overeenkomt met de gerelateerde entiteit. In dit geval moet u de eigenschap op definiëren tussen accolades, zoals wordt getoond in het voorgaande voorbeeld.
Behalve wanneer u werkt met metagegevensentiteiten, zult u geen entiteitseigenschappen instellen voor een object. Met metagegevensentiteiten moet u vaak eigenschappen instellen die een complex type of opsommingswaarden type zijn. Maar dit komt niet vaak voor bij normale bedrijfsentiteiten.
Als u gerelateerde entiteiten maakt, kunt u de waarde van een collection-valued navigatie-eigenschap instellen door middel van een Array, maar is dit een vrij gespecialiseerde bewerking.Meer informatie:In één bewerking gerelateerde entiteiten maken
Entiteitstype-eigenschappen
Als u een entiteit voor een actie plaatst waar het parametertype een basistype voor de entiteit vertegenwoordigt, bijvoorbeeld crmbaseentity EntityType of activitypointer EntityType, moet u mogelijk de eigenschap @odata.type opnemen met de volledige naam van het entiteitstype als waarde. Omdat bijvoorbeeld letter EntityType overneemt van activitypointer, moet u misschien expliciet het type entiteit aangeven met de onderstaande eigenschap en waarde: "@odata.type": "Microsoft.Dynamics.CRM.letter".
Gegevens verzenden voor bijwerkbewerkingen
Als u entiteiten bijwerkt, is het belangrijk alleen eigenschapswaarden in te stellen voor eigenschappen die u wilt bijwerken. Het wordt afgeraden om een entiteit op te halen, eigenschappen van het opgehaalde exemplaar bij te werken en dan dat exemplaar te gebruiken in een bijwerkbewerking. In plaats daarvan is het beter om een nieuw object te maken en nieuwe eigenschappen in te stellen alleen voor de eigenschappen die u wilt bijwerken.
Als u gewoonweg alle eigenschappen van een opgehaalde entiteit overkopieert en de entitiet bijwerkt met PATCH, wordt elke eigenschap die u verzendt als een update beschouwd, zelfs als de waarde hetzelfde als de huidige waarde is. Als u controle hebt ingeschakeld voor de entiteit, geeft het kenmerk aan dat de gegevens zijn gewijzigd terwijl de waarde niet werkelijk is gewijzigd.Meer informatie:Basisbijwerkbewerking
Geretourneerde JSON parseren
Hoewel de maakbewerking in het vorige voorbeeld geen JSON-gegevens retourneert, zullen de meeste bewerkingen die GET gebruiken wel JSON retourneren. Voor de meeste typen geretourneerde gegevens kan de omzetting van JSON naar JavaScript worden gerealiseerd met de volgende coderegel.
var data = JSON.parse(this.response)
Echter, gegevens die datums bevatten zijn problematisch, omdat de datums worden doorgegeven als een tekenreeks, bijvoorbeeld 2015-10-25T17:23:55Z. Als u dit in een JavaScriptDate-object wilt omzetten, moet u de parameter reviver gebruiken voor de functie JSON.parse. Hieronder volgt een voorbeeld van een functie die kan worden gebruikt om datums worden parseren.
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;
};
Als u deze functie wilt toepassen, moet u hem net als parameter toevoegen zoals hier aangegeven.
var data = JSON.parse(this.response,dateReviver)
Een herbruikbare functie maken met callbacks
Wanneer u de code hebt om een bepaalde bewerking uit te voeren, wilt u deze opnieuw gebruiken in plaats van telkens opnieuw dezelfde code te schrijven. De volgende stap is het maken van een JavaScript-bibliotheek met een functie die de bewerking uitvoert met alle mogelijke opties. In dit geval zijn er slechts twee variabelen voor de maakbewerking: naam van de entiteitreeks en de JSON-definitie van de te maken entiteit. In plaats van alle eerder getoonde code te schrijven, kan een bewerking in een functie worden opgenomen die slechts enkele regels code omvat.
Asynchrone bewerkingen met JavaScript hebben traditioneel callback-functies gebruikt om retourwaarden van de asynchrone bewerkingen vast te leggen en logica van het programma voort te zetten. Met gebruik van de code voor de maakbewerking die hierboven is beschreven, is het doel hier dat dezelfde bewerking kan worden uitgevoerd met alleen de volgende code.
MyNameSpace.WebAPI.create("accounts",
{ name: "Sample account" },
function (accountUri) { console.log("Created account with URI: " + accountUri) },
function (error) { console.log(error.message); });
In dit voorbeeld staat MyNameSpace.WebAPI voor de best practice waarin u unieke namen toekent aan de functies die u gebruikt.Meer informatie:Bepaal unieke namen voor uw JavaScript-functies
Voor deze bibliotheek willen we functies voor aanvullende bewerkingen opnemen, zodat er is een mogelijkheid is om herbruikbare persoonlijke functies te hebben om bewerkingen te ondersteunen. De volgende code laat een bibliotheek zien die dit toelicht en een MyNameSpace.WebAPI.create-functie met callbacks bevat.
"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);
Deze bibliotheek demonstreert de best practice waarin u een functie definieert binnen een zelf-uitvoerende anonieme functie (ook wel een zelf-aangeroepen anonieme functie of onmiddellijk-aangeroepen anonieme functie genoemd) en de functie toevoegt aan de naamruimte MyNameSpace.WebAPI. Hierdoor kunt u interne functies definiëren, die niet toegankelijk zijn voor andere code. Alle functies die is gedefinieerd als deel van this, is openbaar en alle functies in de anonieme functie kunnen worden gebruikt door openbare functies maar niet door code die extern is voor de anonieme functie. De code in de functie kan niet door andere code op de pagina worden gewijzigd.
De naamruimte is zodanig gedefinieerd dat deze geen andere overschrijft die dezelfde naamruimte gebruikt, maar wel alle functies overschrijft met dezelfde naam die deel uitmaken van die naamruimte. U kunt afzonderlijke bibliotheken maken die openbare functies toevoegen aan de naamruimte, zolang ze niet dezelfde naam hebben.
De functie MyNameSpace.WebAPI.create biedt de volgende parameters:
Naam |
Beschrijving |
---|---|
entitySetName |
De naam van de entiteitreeks voor het type entiteit dat u wilt maken. |
entity |
Een object met de eigenschappen van de entiteit die u wilt maken. |
successCallback |
De functie die moet worden aangeroepen als de entiteit wordt gemaakt. De URI van de aangemaakte entiteit wordt doorgegeven aan deze functie. |
errorCallback |
De functie die moet worden aangeroepen wanneer een fout optreedt. De fout wordt aan deze functie doorgegeven. |
De code die het XMLHttpRequest-object configureert, is aangepast om deze parameterwaarden te gebruiken, evenals een andere interne helperfunctie getWebAPIPath die de basis-URI van de organisatie zoekt en toevoegt aan de URL zodat deze overeenkomt met de basis-URI voor de Web API aan te passen, zodat u deze niet hoeft toe te voegen. De URI voor de aangemaakte entiteit wordt doorgegeven aan successCallback als deze is gedefinieerd. Net zo wordt de openbare functie errorHandler gebruikt om alle fouten te parseren die worden geretourneerd. De functie errorHandler moet openbaar zijn, omdat deze wordt aangeroepen in de gebeurtenisregistratie voor de gebeurtenis onreadystatechange en dit zich niet bevindt in het bereik van de naamruimte. De functie moet worden opgeroepen met de volledige naam: MyNameSpace.WebAPI.errorHandler.
Een herbruikbare functie maken met promises
Alhoewel callbacks traditioneel voor asynchrone bewerkingen zijn gebruikt, vinden veel ontwikkelaars ze vrij onhandelbaar en lastig te lezen en te debuggen, omdat een reeks asynchrone bewerkingen aan elkaar worden geschakeld en code opleveren die een “pyramid of doom“ vormt, doordat de inspringingen de code, met anonieme functies, steeds verder naar de rechterzijde van de pagina dringen. Hoewel dit probleem kan worden aangepakt door benoemde functies te gebruiken in plaats van anonieme functies, hebben veel ontwikkelaars toch een voorkeur voor promises met hun vele voordelen. Een Promise-object vertegenwoordigt een bewerking die nu nog niet is voltooid, maar waarvan wordt verwacht dat deze in de toekomst wordt voltooid.
Er zijn veel bibliotheken van derden en JavaScript-frameworks die verschillende implementaties van promises bieden.JQuery biedt een gedrag op basis van het CommonJS Promises/A-ontwerp via Deferred object en andere staan op naleving van de Promises/A+-specificatie. Een toelichting op de verschillen tussen deze implementaties valt buiten het bereik van deze topic. Deze sectie heeft alleen maar als doel om te laten zien hoe een helperfunctie voor de Microsoft Dynamics 365 - Web API kan worden geschreven, die een native XMLHttpRequest-object gebruikt, om het native Promise-object gebruikt dat in de meeste moderne browsers is geïmplementeerd die Microsoft Dynamics 365ondersteunt. De volgende browsers hebben een native implementatie van promises: Google Chrome 32, Opera 19, Mozilla Firefox 29, Apple Safari 8 en Microsoft Edge.
Notitie
In Internet Explorer 11 zijn native promises niet geïmplementeerd. Voor andere browsers waarin geen native promises zijn geïmplementeerd, moet u een aparte bibliotheek opnemen om een polyfill te bieden. Een polyfill is code die mogelijkheden biedt niet in native modus door een browser worden geleverd. Er zijn verschillende polyfills of bibliotheken waarmee Internet Explorer 11 promises kan aanbieden: es6-promise, q.js en bluebird.
De voordelen van promises kunnen het beste met een voorbeeld worden uitgelegd. De volgende code gebruikt de callback-versie van MyNameSpace.WebAPI.create om een account te maken en vervolgens drie taken die daaraan worden gekoppeld.
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); });
Negeer voor dit voorbeeld even het feit dat al deze records in één bulkbewerking kunnen worden aangemaakt door middel van diep invoegen (deep insert).Meer informatie:In één bewerking gerelateerde entiteiten maken
De callback-code is niet eenvoudig, omdat hij in het midden van het codeblok eindigt. Met promises kunt u dezelfde records aanmaken met de volgende code.
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); });
Door promises te gebruiken, blijft de stroom van de code bewaard en kunnen alle voorkomende fouten worden afgevangen in een enkele catch-functie.
Om de functie met callbacks om te zetten naar een functie met promises, verwijdert u de callback-parameters en retourneert u een licht gewijzigde XMLHttpRequest, zoals weergegeven in het volgende codevoorbeeld.
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));
});
Niet alleen worden de callback-parameters verwijderd, ook wordt XMLHttpRequest opgenomen in de Promise. Resultaten of fouten worden dan niet meer doorgegeven aan fout-callbacks, maar in plaats daarvan aan resolve- of reject-parameters. De volgende code vertegenwoordigt de volledige JavaScript-bibliotheek die de functie MyNameSpace.WebAPI.create bevat. U hoeft alleen maar meer herbruikbare Web API-bewerkingen toe te voegen door middel van hetzelfde patroon.
"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);
Zie ook
De web-API van Microsoft Dynamics 365 gebruiken
Werken met Dynamics 365-gegevens met behulp van webresources
Bewerkingen uitvoeren met de web-API
Web-API-voorbeelden (JavaScript op de client)
Een toepassing met één pagina door middel van OAuth met Cross-Origin Resource Sharing verbinding laten maken met Microsoft Dynamics 365
Microsoft Dynamics 365
© 2017 Microsoft. Alle rechten voorbehouden. Auteursrecht