Gennemgang: Bruge moderne App SOAP-slutpunkt sammen med JavaScript
Udgivet: november 2016
Gælder for: Dynamics CRM 2015
Denne gennemgang viser oprettelsen af et JavaScript-bibliotek til brug sammen med SOAP-slutpunktet med webressourcer. På nuværende tidspunkt har Microsoft Dynamics CRM 2015 og opdatering til Microsoft Dynamics CRM Online 2015 ikke JavaScript-biblioteker, som du kan bruge til at kalde meddelelser, der er tilgængelige ved hjælp af SOAP-slutpunktet. Denne gennemgang viser, hvordan du opretter et JScript-bibliotek for Assign-meddelelsen ved hjælp af organisationsservicens Execute-metode. Alle andre meddelelser, der bruger Execute-metoden, følger et lignende mønster. Oprettelse af et JavaScript-bibliotek for alle meddelelser vil kræve en lignende proces. Du kan se et eksempel på et JavaScript-bibliotek udviklet ved hjælp af denne proces i Eksempel: Hent objektmetadata ved hjælp af JavaScript.
Bemærk
Du kan se et komplet JavaScript-eksempelbibliotek, som anvender SOAP-slutpunktet, i Sdk.Soap.js-biblioteket. Sdk.Soap.js-biblioteket blev oprettet ved hjælp af den proces, der er beskrevet her.
Processen består af hentning af HTTP-anmodninger og -svar, der sendes fra et administreret kodekonsolprogram og derefter oprettelse af et JavaScript-bibliotek med funktioner til at sende de samme anmodninger og behandle lignende svar. Det er almindeligt at bruge et program som Fiddler til at hente HTTP-anmodninger og -svar, men Fiddler og lignende programmer kan ikke hente den krypterede HTTP-trafik, der anvendes til Microsoft Dynamics CRM 2015 i det lokale miljø.SOAPLogger-eksempelløsningen henter HTTP-anmodninger, før de er krypteret, og HTTP-svar, når de er blevet dekrypteret.SOAPLogger-eksempelløsningen fungerer i både Microsoft Dynamics CRM 2015 og opdatering til Microsoft Dynamics CRM Online 2015.SOAPLogger bortfiltrerer også al HTTP-trafik, der ikke er relevant for oprettelsen af et JScript-bibliotek.
I denne gennemgang bliver du guidet gennem følgende opgaver:
I Hent eksempel på HTTP-anmodninger og -svar skal du redigere Run-metoden i SOAPLogger-eksemplet for at udføre en Assign-anmodning ved hjælp af administreret kode.
Derefter skal du køre SOAPLogger-løsningen for at generere oplysninger om de HTTP-anmodninger, der er sendt, og modtagne svar.
I Oprette et JScript-bibliotek skal du oprette et JScript-bibliotek, der er baseret på den hentede HTTP-trafik, for at sende og modtage Assign-meddelelsen ved hjælp af organisationsservicens Execute-metode.
I Test af JScript-bibliotek skal du teste JScript-biblioteket for at bekræfte, at det kan bruges til at tildele poster.
I Oprette en interaktiv webressource skal du oprette en HTML-webressource, der bruger biblioteket til at tildele en udvalgt bruger Account-poster, som vist på følgende skærmbillede.
En administreret løsning, der repræsenterer denne fuldførte gennemgang, er tilgængelig i SDK-pakken på SDK\SampleCode\JS\SOAPForJScript\SOAPEndpointforJScript_1_0_0_1_managed.zip. Hent SDK-pakken til Microsoft Dynamics CRM. Du kan installere (importere) denne løsning og se de færdige resultater. Fordi det er en administreret løsning, kan du nemt fjerne (slette) den for helt at fjerne den fra dit system.
SDK\SampleCode\JS\SOAPForJScript\SOAPForJScript.sln er en Visual Studio-løsning indeholdende HTML- og JS-filer med koden i denne gennemgang.
Forudsætninger
Du skal være i stand til at skrive administreret kode, der svarer til de funktioner, du vil udføre ved hjælp af JScript.
Du skal have adgang til det SOAPLogger-eksempel, der er tilgængeligt via SDK-downloadfilerne på SDK\SampleCode\CS\Client\SOAPLogger.
Bemærk
Eksemplet på SOAPLogger i Microsoft Dynamics CRM SDK er kun tilgængeligt for Microsoft Visual C#. Hvis du foretrækker en Microsoft Visual Basic .NET-version, skal du se Microsoft Dynamics CRM VB.Net SoapLogger på CodePlex.
Du skal have Microsoft Visual Studio for at køre SOAPLogger-løsningen.
Du har brug for et godt kendskab til JScript og asynkron programmering ved hjælp af XMLHttpRequest.
Hent eksempel på HTTP-anmodninger og -svar
Rediger SOAPLogger-løsningen for at tilføje kode og udføre AssignRequest ved hjælp af SoapLoggerOrganizationService.SoapLoggerOrganizationService arver fra IOrganizationService og tilføjer evnen til at generere en logfil, der viser den relevante HTTP-trafik, der sendes og modtages.
Brug Microsoft Visual Studio til at åbne SOAPLogger-løsningen, der er placeret på SDK\SampleCode\CS\Client\SOAPLogger\SOAPLogger.sln.
I filen SoapLogger.cs skal du finde Run-metoden som vist i følgende eksempel:
public void Run(ServerConnection.Configuration serverConfig) { try { // Connect to the Organization service. // The using statement assures that the service proxy will be properly disposed. using (_serviceProxy = new OrganizationServiceProxy(serverConfig.OrganizationUri, serverConfig.HomeRealmUri, serverConfig.Credentials, serverConfig.DeviceCredentials)) { // This statement is required to enable early-bound type support. _serviceProxy.EnableProxyTypes(); IOrganizationService service = (IOrganizationService)_serviceProxy; using (StreamWriter output = new StreamWriter("output.txt")) { SoapLoggerOrganizationService slos = new SoapLoggerOrganizationService(serverConfig.OrganizationUri, service, output); //Add the code you want to test here: // You must use the SoapLoggerOrganizationService 'slos' proxy rather than the IOrganizationService proxy you would normally use. } } } // Catch any service fault exceptions that Microsoft Dynamics CRM throws. catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>) { // You can handle an exception here or pass it back to the calling method. throw; } }
Identificer et gyldigt id for en SystemUser-post og id'et for en Account-post, der ikke er tildelt den pågældende bruger.
Tip
For at få Id fra enhver post i programmet skal du åbne den pågældende post og bruge kommandoen Kopiér et link. Indsæt linket i Notesblok, og isoler den del, der indeholder id-parameterværdien, med undtagelse af de kodede parenteser ("%7b" = "{") og ("%7d" = "}"). For eksempel repræsenterer følgende en URL til en Account-post.<your organization root url>/main.aspx?etc=1&id=%7bF2CA52DE-552D-E011-A8FB-00155DB059BE%7d&pagetype=entityrecord>. Værdien af id'et er F2CA52DE-552D-E011-A8FB-00155DB059BE.
Brug disse værdier til at erstatte pladsholderværdierne i nedenstående kode, og føj koden til metoden run som anført. Du kan finde flere oplysninger om Assign-anmodningen i AssignRequest.
Guid userId = new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); Guid accountId = new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); AssignRequest assign = new AssignRequest{Assignee = new EntityReference(SystemUser.EntityLogicalName, userId),Target = new EntityReference(Account.EntityLogicalName, accountId)}; AssignResponse assignResp = (AssignResponse)slos.Execute(assign);
Tryk på F5 for at foretage fejlfinding af programmet. Angiv oplysninger om serveren og dine legitimationsoplysninger til godkendelse. Hvis koden køres korrekt, tildeles firmaposten nu den bruger, du har angivet. Find flere oplysninger om kørsel af eksempelkode i Kørsel af et simpelt program ved hjælp af Microsoft Dynamics CRM 2015-webtjenester
I SOAPLogger-projektmappen skal du gå til mappen bin og Debug for at søge efter output.txt-filen.
Åbn output.txt-filen, og gennemgå indholdet. Det skal ligne følgende bortset fra, at de faktiske GUID-værdier skal erstatte pladsholderen "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”.
HTTP REQUEST -------------------------------------------------- POST <your server root>/XRMServices/2011/Organization.svc/web Content-Type: text/xml; charset=utf-8 SOAPAction: https://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute <s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <Execute xmlns="https://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <request i:type="b:AssignRequest" xmlns:a="https://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="https://schemas.microsoft.com/crm/2011/Contracts"> <a:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic"> <a:KeyValuePairOfstringanyType> <c:key>Target</c:key> <c:value i:type="a:EntityReference"> <a:Id>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</a:Id> <a:LogicalName>account</a:LogicalName> <a:Name i:nil="true" /> </c:value> </a:KeyValuePairOfstringanyType> <a:KeyValuePairOfstringanyType> <c:key>Assignee</c:key> <c:value i:type="a:EntityReference"> <a:Id>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</a:Id> <a:LogicalName>systemuser</a:LogicalName> <a:Name i:nil="true" /> </c:value> </a:KeyValuePairOfstringanyType> </a:Parameters> <a:RequestId i:nil="true" /> <a:RequestName>Assign</a:RequestName> </request> </Execute> </s:Body> </s:Envelope> -------------------------------------------------- HTTP RESPONSE -------------------------------------------------- <s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <ExecuteResponse xmlns="https://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ExecuteResult i:type="b:AssignResponse" xmlns:a="https://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="https://schemas.microsoft.com/crm/2011/Contracts"> <a:ResponseName>Assign</a:ResponseName> <a:Results xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic" /> </ExecuteResult> </ExecuteResponse> </s:Body> </s:Envelope> --------------------------------------------------
Bemærk placeringen af de variabler, du sendte ved hjælp af koden, herunder det logiske navn på Account-objektet.
Du har hentet de relevante dele af den HTTP-trafik, der hat udfør tildelingshandlingen.
Oprette et JScript-bibliotek
Nøjagtigt, hvordan du ønsker at oprette biblioteket, er op til dig. Hvert JScript bør indeholde følgende elementer:
En strategi for at sikre, at navnet på en funktion er entydigt.
Mulighed for at hente URL til SOAP-slutpunktet for webressourcer.
Forskellige metoder til at sende anmodningen og modtage svar asynkront.
En funktion til at behandle eventuelle WCF-fejl, der returneres.
Følgende JScript-bibliotek indeholder disse elementer og omfatter de assignRequest- og assignResponse-funktioner, der skal bruge Assign-meddelelsen, baseret på HTTP-anmodninger og -svar ved hjælp af SOAPLogger.
if (typeof (SDK) == "undefined")
{ SDK = { __namespace: true }; }
//This will establish a more unique namespace for functions in this library. This will reduce the
// potential for functions to be overwritten due to a duplicate name when the library is loaded.
SDK.SOAPSamples = {
_getClientUrl: function () {
///<summary>
/// Returns the URL for the SOAP endpoint using the context information available in the form
/// or HTML Web resource.
///</summary
var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
var clientUrl = "";
if (typeof GetGlobalContext == "function") {
var context = GetGlobalContext();
clientUrl = context.getClientUrl();
}
else {
if (typeof Xrm.Page.context == "object") {
clientUrl = Xrm.Page.context.getClientUrl();
}
else
{ throw new Error("Unable to access the server URL"); }
}
return clientUrl + OrgServicePath;
},
assignRequest: function (Assignee, Target, Type, successCallback, errorCallback) {
///<summary>
/// Sends the Assign Request
///</summary>
this._parameterCheck(Assignee, "GUID", "The SDK.SOAPSamples.assignRequest method Assignee parameter must be a string Representing a GUID value.");
///<param name="Assignee" Type="String">
/// The GUID representing the System user that the record will be assigned to.
///</param>
this._parameterCheck(Target, "GUID", "The SDK.SOAPSamples.assignRequest method Target parameter must be a string Representing a GUID value.");
///<param name="Target" Type="String">
/// The GUID representing the user-owned entity record that will be assigne to the Assignee.
///</param>
this._parameterCheck(Type, "String", "The SDK.SOAPSamples.assignRequest method Type parameter must be a string value.");
Type = Type.toLowerCase();
///<param name="Type" Type="String">
/// The Logical name of the user-owned entity. For example, 'account'.
///</param>
if (successCallback != null)
this._parameterCheck(successCallback, "Function", "The SDK.SOAPSamples.assignRequest method successCallback parameter must be a function.");
///<param name="successCallback" Type="Function">
/// The function to perform when an successfult response is returned.
///</param>
this._parameterCheck(errorCallback, "Function", "The SDK.SOAPSamples.assignRequest method errorCallback parameter must be a function.");
///<param name="errorCallback" Type="Function">
/// The function to perform when an error is returned.
///</param>
//The request is simply the soap envelope captured by the SOAPLogger with variables added for the
// values passed. All quotations must be escaped to create valid JScript strings.
var request = [];
request.push("<s:Envelope xmlns:s=\"https://schemas.xmlsoap.org/soap/envelope/\">");
request.push("<s:Body>");
request.push("<Execute xmlns=\"https://schemas.microsoft.com/xrm/2011/Contracts/Services\"");
request.push(" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">");
request.push("<request i:type=\"b:AssignRequest\"");
request.push(" xmlns:a=\"https://schemas.microsoft.com/xrm/2011/Contracts\"");
request.push(" xmlns:b=\"https://schemas.microsoft.com/crm/2011/Contracts\">");
request.push("<a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">");
request.push("<a:KeyValuePairOfstringanyType>");
request.push("<c:key>Target</c:key>");
request.push("<c:value i:type=\"a:EntityReference\">");
request.push("<a:Id>" + this._xmlEncode(Target) + "</a:Id>");
request.push("<a:LogicalName>" + this._xmlEncode(Type) + "</a:LogicalName>");
request.push("<a:Name i:nil=\"true\" />");
request.push("</c:value>");
request.push("</a:KeyValuePairOfstringanyType>");
request.push("<a:KeyValuePairOfstringanyType>");
request.push("<c:key>Assignee</c:key>");
request.push("<c:value i:type=\"a:EntityReference\">");
request.push("<a:Id>" + this._xmlEncode(Assignee) + "</a:Id>");
request.push("<a:LogicalName>systemuser</a:LogicalName>");
request.push("<a:Name i:nil=\"true\" />");
request.push("</c:value>");
request.push("</a:KeyValuePairOfstringanyType>");
request.push("</a:Parameters>");
request.push("<a:RequestId i:nil=\"true\" />");
request.push("<a:RequestName>Assign</a:RequestName>");
request.push("</request>");
request.push("</Execute>");
request.push("</s:Body>");
request.push("</s:Envelope>");
var req = new XMLHttpRequest();
req.open("POST", SDK.SOAPSamples._getClientUrl(), true)
// Responses will return XML. It isn't possible to return JSON.
req.setRequestHeader("Accept", "application/xml, text/xml, */*");
req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
req.setRequestHeader("SOAPAction", "https://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
req.onreadystatechange = function () { SDK.SOAPSamples.assignResponse(req, successCallback, errorCallback); };
req.send(request.join(""));
},
assignResponse: function (req, successCallback, errorCallback) {
///<summary>
/// Recieves the assign response
///</summary>
///<param name="req" Type="XMLHttpRequest">
/// The XMLHttpRequest response
///</param>
///<param name="successCallback" Type="Function">
/// The function to perform when an successfult response is returned.
/// For this message no data is returned so a success callback is not really necessary.
///</param>
///<param name="errorCallback" Type="Function">
/// The function to perform when an error is returned.
/// This function accepts a JScript error returned by the _getError function
///</param>
if (req.readyState == 4) {
req.onreadystatechange = null; //avoids memory leaks
if (req.status == 200) {
if (successCallback != null)
{ successCallback(); }
}
else {
errorCallback(SDK.SOAPSamples._getError(req.responseXML));
}
}
},
_getError: function (faultXml) {
///<summary>
/// Parses the WCF fault returned in the event of an error.
///</summary>
///<param name="faultXml" Type="XML">
/// The responseXML property of the XMLHttpRequest response.
///</param>
var errorMessage = "Unknown Error (Unable to parse the fault)";
if (typeof faultXml == "object") {
try {
var bodyNode = faultXml.firstChild.firstChild;
//Retrieve the fault node
for (var i = 0; i < bodyNode.childNodes.length; i++) {
var node = bodyNode.childNodes[i];
//NOTE: This comparison does not handle the case where the XML namespace changes
if ("s:Fault" == node.nodeName) {
for (var j = 0; j < node.childNodes.length; j++) {
var faultStringNode = node.childNodes[j];
if ("faultstring" == faultStringNode.nodeName) {
errorMessage = faultStringNode.textContent;
break;
}
}
break;
}
}
}
catch (e) { };
}
return new Error(errorMessage);
},
_xmlEncode: function (strInput) {
var c;
var XmlEncode = '';
if (strInput == null) {
return null;
}
if (strInput == '') {
return '';
}
for (var cnt = 0; cnt < strInput.length; cnt++) {
c = strInput.charCodeAt(cnt);
if (((c > 96) && (c < 123)) ||
((c > 64) && (c < 91)) ||
(c == 32) ||
((c > 47) && (c < 58)) ||
(c == 46) ||
(c == 44) ||
(c == 45) ||
(c == 95)) {
XmlEncode = XmlEncode + String.fromCharCode(c);
}
else {
XmlEncode = XmlEncode + '&#' + c + ';';
}
}
return XmlEncode;
},
_parameterCheck: function (parameter, type, errorMessage) {
switch (type) {
case "String":
if (typeof parameter != "string") {
throw new Error(errorMessage);
}
break;
case "Function":
if (typeof parameter != "function") {
throw new Error(errorMessage);
}
break;
case "EntityFilters":
var found = false;
for (x in this.EntityFilters) {
if (this.EntityFilters[x] == parameter) {
found = true;
break;
}
}
if (!found) {
throw new Error(errorMessage);
}
break;
case "Boolean":
if (typeof parameter != "boolean") {
throw new Error(errorMessage);
}
break;
case "GUID":
var re = new RegExp("[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}");
if (!(typeof parameter == "string" && re.test(parameter))) {
throw new Error(errorMessage);
}
break;
default:
throw new Error("An invalid type parameter value was passed to the SDK.MetaData._parameterCheck function.");
break;
}
},
__namespace: true
};
Test af JScript-bibliotek
Når du har forberedt dit JScript-bibliotek, bør du udføre nogle indledende test for at bekræfte, at det fungerer som forventet. Da godkendelse kun udføres i programmet, skal du oprette en scriptwebressource i Microsoft Dynamics CRM 2015 ved hjælp af indholdet af dit bibliotek. For at bruge funktionerne i biblioteket skal du have et brugergrænsefladeelementet, enten en formular eller en HTML-webressource, til at kalde JScript-funktioner i dit bibliotek. I følgende procedure beskrives brug af en enkel HTML-webressource til at teste funktionerne i biblioteket.
Opret et HTML-dokument ved hjælp af følgende kode:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Assign Test</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script> <script src="Scripts/SDK.REST.js" type="text/javascript"></script> <script src="Scripts/SDK.SOAPSample.Assign.js" type="text/javascript"></script> <script type="text/javascript"> var resultsArea = null; var userid; var accountid; var userLookup; var accountLookup; var accountsToShow = 10; var usersToShow = 10; var users = []; var accounts = []; document.onreadystatechange = function () { ///<summary> /// Initializes the sample when the document is ready ///</summary> if (document.readyState == "complete") { userid = document.getElementById("userid"); accountid = document.getElementById("accountid"); resultsArea = document.getElementById("results"); userLookup = document.getElementById("userLookup"); accountLookup = document.getElementById("accountLookup"); populateUserLookup(); populateAccountLookup(); } } function testAssign() { //The field to enter the user id of the person the account record should be assigned to. userid.value; // The field to enter the account id of the account record to assign to the user accountid.value; // Since the response does not include any data to parse, simply display a success message. var successCallback = function () { setText(resultsArea, "Success!"); }; // Display the error from the message passed back from the response. var errorCallback = function (error) { setText(resultsArea,"errorCallback error: "+error.message); }; // Call the function try { SDK.SOAPSamples.assignRequest(userid.value, accountid.value, "account", successCallback, errorCallback); } catch (e) { setText(resultsArea, e.message); } } function populateUserLookup() { SDK.REST.retrieveMultipleRecords("SystemUser", "$top=" + usersToShow + "&$select=FullName,SystemUserId&$filter=FullName ne 'INTEGRATION' and FullName ne 'SYSTEM'", function (results) { for (var i = 0; i < results.length; i++) { users.push(results[i]); } }, function (error) { alert(error.message); }, function () { for (var i = 0; i < users.length; i++) { var user = users[i]; userLookup.options[i] = new Option(user.FullName, user.SystemUserId); } userid.value = userLookup.options[0].value; userLookup.onchange = function () { userid.value = userLookup.options[userLookup.selectedIndex].value; clearMessage(); }; }); } function populateAccountLookup() { SDK.REST.retrieveMultipleRecords("Account", "$top=" + accountsToShow + "&$select=AccountId,Name,OwnerId", function (results) { for (var i = 0; i < results.length; i++) { accounts.push(results[i]); } }, function (error) { alert(error.message); }, function () { for (var i = 0; i < accounts.length; i++) { var account = accounts[i]; accountLookup.options[i] = new Option(account.Name + " : " + account.OwnerId.Name, account.AccountId); } accountid.value = accountLookup.options[0].value; accountLookup.onchange = function () { accountid.value = accountLookup.options[accountLookup.selectedIndex].value; clearMessage(); }; }); } function clearMessage() { setText(resultsArea, ""); } function setText(element, text) { if (typeof (element.innerText) != "undefined") { element.innerText = text; } else { element.textContent = text; } } </script> </head> <body> <table> <tr> <td> <label for="userid"> SystemUserId:</label> </td> <td> <input id="userid" type="text" style="width:270px;" /> </td> <td> <select id="userLookup"></select> </td> </tr> <tr> <td> <label for="accountid"> AccountId:</label> </td> <td> <input id="accountid" type="text" style="width:270px;" /> </td> <td> <select id="accountLookup"></select> </td> </tr> </table> <button id="testAssign" title="Click this button to test the testAssign function." onclick="testAssign();"> Test Assign</button> <div id="results"> &nbsp;</div> </body> </html>
Opret en HTML-webressource ved hjælp af denne kode. Det fulde navn på HTML-webressourcen afhænger af tilpasningspræfikset for din løsning. Under forudsætning af, at tilpasningspræfikset i din løsning er "new", skal du kalde denne HTML-webressource "new_/AssignTest.htm". Det specifikke tilpasningspræfiks, der bruges, har ingen indflydelse på dette eksempel, så længe alle webressourcer bruger samme tilpasningspræfiks.
Opret en scriptwebressource ved hjælp af indholdet af JScript-biblioteket. Giv denne webressource navnet "new_/Scripts/SDK.SOAPSample.Assign.js".
Udgiv alle tilpasninger, og åbn new_/AssignTest.htm-webressourcen. Klik på knappen Eksempel.
Brug samme proces, som du brugte i trin tre i No text is specified for bookmark or legacy link 'c9a435ab-a4cb-4e0c-9ef7-b7ba66278407#BKMK_EditSoapLogger'.-proceduren, til at identificere gyldige id-værdier for systembruger- og firmapost, og indsæt dem på siden. Klik derefter på Test Assign for at bekræfte, at du får et svar med succes.
Du skal angive ugyldige værdier for at bekræfte, at fejlen fortolkes korrekt.
Hvis du f.eks. skriver "I en butik (eksempel)" i stedet for id'et for et firma kan du forvente følgende fejlmeddelelse:
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter https://schemas.microsoft.com/xrm/2011/Contracts/Services:request. The InnerException message was 'There was an error deserializing the object of type Microsoft.Xrm.Sdk.OrganizationRequest. The value 'A Store (sample)' cannot be parsed as the type 'Guid'.'. Please see InnerException for more details.
Hvis der ikke er en firmapost for AccountId, kan du forvente denne meddelelse:
Account With Id = 883c3084-1f2f-e011-ad66-00155dba3814 Does Not Exist
Oprette en interaktiv webressource
Næste trin er at bruge dit JScript-bibliotek i et formularscript, en kommando på båndet eller en webressource. Følgende kode repræsenterer for eksempel en HTML-webressource, der bruger REST-slutpunktet til at hente lister over SystemUser- og Account-poster, der indeholder en interaktiv brugergrænseflade for at tildele brugere firmaposter. Denne side bruger Scripts/SDK.SOAPSample.Assign.js-scriptwebressourcen og en scriptwebressource ved hjælp af json2.js-biblioteket for at understøtte brugen af JSON-objekter med REST-slutpunktet. Biblioteket SDK.REST.js bruges til at hente data ved hæjlp af REST-slutpunktet.
<html lang="en-us">
<head>
<title>Assign Accounts Page</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<style type="text/css">
body
{
font-family: Segoe UI;
background-color: #EFF2F6;
}
.dataTable
{
border-collapse: collapse;
border: 1px solid black;
}
.tableHead
{
background-color: #C0C0C0;
width: 100%;
}
.grid
{
border-bottom: 1px solid black;
}
td
{
padding-left: 5px;
padding-right: 5px;
}
#accountList
{
background-color: White;
}
#checkboxCol
{
border-right: 1px solid black;
width: 20px;
}
#nameCol
{
border-right: 1px solid black;
width: 300px;
}
#ownerCol
{
width: 300px;
}
</style>
<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="Scripts/SDK.REST.js" type="text/javascript"></script>
<script src="Scripts/SDK.SOAPSample.Assign.js" type="text/javascript"></script>
<script type="text/javascript">
//Set variables for page elements
var userSelect; //The select control used to select the user to assign records to.
var accountList; //The tbody element that rows will be added to for each retrieved account
var selectAll; //The checkbox to select all the retrieved accounts
var btnAssign; //The button to assign assign the accounts
var tableCaption; //A label hidden on load
var dataTable; //the table element hidden on load
var alertFlag; // Alert flag to indicate the changes
var users = []; //SystemUser records retrieved
var accounts = []; //Accounts not assigned to the currently selected user.
var accountsToShow = 5;
var suppressRetrievedAccountsAlert = false;
var accountsToAssign = [];
var userId = null;
function startSample() {
///<summary>
/// Starts the sample
///</summary>
alertFlag = document.getElementById("dispalert");
userSelect = document.getElementById("userList");
//A new set of a 5 accounts will be retrieved when the user changes
userSelect.onchange = getAccounts;
accountList = document.getElementById("accountList");
selectAll = document.getElementById("selectAll");
//When the select all checkbox is clicked, toggle the selection for each row of the table.
selectAll.onclick = toggleSelectAllRecords;
btnAssign = document.getElementById("btnAssign");
//Add a helper function to enable or disable the assign button.
btnAssign.setEnabled = setEnabled;
//Set the event handler to the Assign button
btnAssign.onclick = assignAccounts;
tableCaption = document.getElementById("tableCaption");
dataTable = document.getElementById("dataTable");
var divSample = document.getElementById("divSample");
//Load the list of users
getUsers();
divSample.style.display = "block";
document.getElementById("btnStart").setAttribute("disabled", "disabled");
}
function getUsers() {
SDK.REST.retrieveMultipleRecords("SystemUser",
"$select=FullName,SystemUserId&$filter=FullName ne 'INTEGRATION' and FullName ne 'SYSTEM'",
function (results) {
for (var i = 0; i < results.length; i++) {
users.push(results[i]);
}
},
function (error) {
showMessage(error.message);
},
function () {
if (users.length > 1) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
userSelect.options[i + 1] = new Option(user.FullName, user.SystemUserId);
}
userSelect.removeAttribute("disabled");
if (alertFlag.checked == true) {
alert(users.length + " system users retrieved");
}
}
else {
var notification = "This sample requires that more than one user is available in the organization.";
showMessage(notification);
if (alertFlag.checked == true) {
alert("This sample requires that more than one user is available in the organization.");
}
}
});
}
function getAccounts() {
//Clear out any records displayed in the table
clearAccountList();
var selectedUserId = userSelect.options[userSelect.selectedIndex].value;
if (selectedUserId != "none") {
SDK.REST.retrieveMultipleRecords("Account",
"$top=" + accountsToShow + "&$select=AccountId,Name,OwnerId&$filter=OwnerId/Id ne (guid'" + encodeURIComponent(selectedUserId) + "')",
function (results) {
accounts.length = 0;
for (var i = 0; i < results.length; i++) {
accounts.push(results[i]);
}
},
function (error) {
showMessage(error.message);
},
function () {
//onComplete
if (accounts.length > 0) {
for (var i = 0; i < accounts.length; i++) {
var account = accounts[i];
var row = document.createElement("tr");
row.setAttribute("class", "grid");
row.setAttribute("id", account.AccountId);
var checkboxCell = document.createElement("td");
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.onclick = validateAssignButton;
checkboxCell.appendChild(checkbox);
row.appendChild(checkboxCell);
var nameCell = document.createElement("td");
setText(nameCell, account.Name);
row.appendChild(nameCell);
var userNameCell = document.createElement("td");
setText(userNameCell, account.OwnerId.Name);
row.appendChild(userNameCell);
accountList.appendChild(row);
}
if (alertFlag.checked == true && !suppressRetrievedAccountsAlert) {
alert(accounts.length + " account records retrieved.");
suppressRetrievedAccountsAlert = false;
}
}
else {
//If no records are returned display a message.
clearAccountList();
var row = document.createElement("tr");
var cell = document.createElement("td");
cell.setAttribute("colSpan", "3");
setText(cell, "No Accounts were retrieved");
row.appendChild(cell);
accountList.appendChild(row);
}
//Show any of the UI elements that are initially hidden
setVisibleUIElements(true);
});
}
else { setVisibleUIElements(false); }
}
function assignAccounts() {
///<summary>
/// queues the selected accounts to be assigned sequentially
///</summary>
userId = userSelect.options[userSelect.selectedIndex].value;
var checkboxes = accountList.getElementsByTagName("input");
for (var i = checkboxes.length - 1; i >= 0; i--) {
if (checkboxes[i].checked) {
var accountId = checkboxes[i].parentElement.parentElement.id;
accountsToAssign.push(accountId);
}
}
assignNextAccount();
selectAll.checked = false;
}
function assignNextAccount() {
/// <summary>Assigns the queued accounts</summary>
//Prevents a generic SQL error that can occur when too many assign requests occur in rapid succession
if (accountsToAssign.length > 0) {
SDK.SOAPSamples.assignRequest(userId, accountsToAssign.shift(), "account", function () {
assignNextAccount();
}, function (error) {
showMessage("There was an error assigning the account with Id :" + accountId + ". The error message is " + error.message + ".");
assignNextAccount();
});
}
else {
suppressRetrievedAccountsAlert = true;
getAccounts();
btnAssign.setEnabled(false)
if (alertFlag.checked == true) {
alert("Record assignment completed and next set of records retrieved.");
}
}
}
function showMessage(message) {
///<summary>
/// Helper function to display message on the page if necessary.
///</summary
var dvMessage = document.createElement("div");
dvMessage.innerHTML = SDK.SOAPSamples._xmlEncode(message);
document.getElementById("status").appendChild(dvMessage);
}
function clearAccountList() {
///<summary>
/// Helper function remove rows from the Account List table.
///</summary
for (var i = (accountList.rows.length - 1) ; i >= 0; i--) {
accountList.deleteRow(i);
}
accounts.length = 0;
}
function toggleSelectAllRecords() {
///<summary>
/// Helper function to toggle all selected rows in the account list table.
///</summary
var checkboxes = accountList.getElementsByTagName("input");
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = this.checked;
}
btnAssign.setEnabled(this.checked);
}
function validateAssignButton() {
///<summary>
/// Helper function to enable the Assign Records button when rows are selected.
///</summary
if (this.checked == true)
{ btnAssign.setEnabled(true); }
else {
selectAll.checked = false;
var checkboxes = accountList.getElementsByTagName("input");
var checked = false;
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked == true) {
checked = true;
break;
}
}
btnAssign.setEnabled(checked);
}
}
function setEnabled(bool) {
///<summary>
/// Helper method attached to the Assign button to make it easier to enable/disable the button.
///</summary
if (bool)
{ this.removeAttribute("disabled"); }
else
{ this.setAttribute("disabled", "disabled"); }
}
function setVisibleUIElements(display) {
///<summary>
/// Helper function to show those UI elements initially hidden.
///</summary
if (display) {
show(tableCaption);
show(dataTable);
show(btnAssign);
}
else {
hide(tableCaption);
hide(dataTable);
hide(btnAssign);
}
}
function show(element) {
if (element.tagName.toLowerCase() == "table") {
element.style.display = "table";
}
else {
element.style.display = "block";
}
}
function hide(element) {
element.style.display = "none";
}
// setText mitigate differences in how browsers set or get text content.
function setText(node, text) {
if (typeof (node.innerText) != "undefined") {
node.innerText = text;
}
else {
node.textContent = text;
}
}
</script>
</head>
<body>
<h1>
Assign Accounts Sample
</h1>
<p>
This page requires JavaScript and will update dynamically.
</p>
<p>
<input id="dispalert" name="dispalert" type="checkbox" value="alert" /><label for="dispalert">Display alert window when data changes.</label>
</p>
<p>
Click the <b>Start</b> button to begin the sample.
</p>
<input type="button" id="btnStart" name="btnStart" value="Start" onclick="startSample()" />
<div id="divSample" style="display: none">
<label for="userList">
User:
</label>
<select id="userList" name="userList" title="Select a system user from this list." disabled>
<option value="none">Select a User...</option>
</select>
<p id="tableCaption" style="display: none;">
Top 5 Accounts not assigned to the selected user:
</p>
<table class="dataTable" id="dataTable" style="display: none; width: 100%;">
<thead>
<tr class="tableHead">
<th scope="col" id="checkboxCol">
<input id="selectAll" name="selectAll" title="Select this to select all records" type="checkbox" /><label for="selectAll">Select&nbsp;All</label>
</th>
<th scope="col" id="nameCol">
Name
</th>
<th scope="col" id="ownerCol">
Owner
</th>
</tr>
</thead>
<tbody id="accountList"></tbody>
</table>
<label style="display: none;" for="btnAssign">
Click to assign selected records
</label>
<button id="btnAssign" name="btnAssign" disabled style="display: none; float: right;">
Assign Records
</button>
<label style="display: none;" for="btnAssign">
Click to assign selected records
</label>
<button id="Button1" name="btnAssign" disabled style="display: none; float: right;">
Assign Records
</button>
</div>
<div id="status">
&nbsp;
</div>
</body>
</html>
Se også
AssignRequest
Eksempel: Hent objektmetadata ved hjælp af JavaScript
Bruge det moderne apps SOAP-slutpunkt til moderne programmer med webressourcer
Bruge webtjenestedata i webressourcer (OData og moderne App SOAP-slutpunkt)
Skriv program- og serverudvidelser
JavaScript-biblioteker til Microsoft Dynamics CRM 2015
Teknisk artikel: Brug af grupperede indstillinger sammen med slutpunktet REST – JScript
© 2017 Microsoft. Alle rettigheder forbeholdes. Ophavsret