Calling “CRM” Web services using JScript



Since I wrote the post about calling “any” web service from CRM forms (BTW, that is my most viewed post to date!), folks have written to me and asked “what the heck, what about calling “CRM” web services, hello”?


Calling CRM web services from JScript is not the best programming experience that you could imagine, mostly because it is not much fun using strongly typed classes in Jscript code (Atlas solves that problem to a great degree, more on this later) whereas you do get a great developer experience inside Visual Studio with strongly typed classes.


So there are two options to call CRM web services from CRM forms (or from any html/aspx pages using JScript) 1) use a intermediate proxy web service with a simple interface that masks the complexy of strongly typed classes. Such web service can have complex code and calls CRM web services using our WSDL.  This approach may work for many of your scenarios where performance is not as critical as developer experience 2) call CRM web services directly from CRM form using SOAP messages (there is a third option to use Atlas. Michael who is one of our dev leads and I are still thinking about the best way to get that to work, stay tuned).  This approach is less developer friendly to work with since you have to create a SOAP message in JScript, send it to the CRM server and parse the returned XML to get the data that you need, but it should be faster than calling teh intermediate service.  Here is a sample to show you how to do this using JScript and POST.


This sample directly calls CRM web service RetrieveMultiple method and passes a QueryByAttribute object.  The query condition is to retrieve all the accounts that are in the state of Washington (address1_stateorprovince = WA).


First thing I did was to enable tracing and capture the SOAP message for RetrieveMultiple passing in a QueryByAttribute. The tracing file includes both requests and responses. Next, I added the SOAP request string to my POST request on the Jscript code. I post the SOAP message to CRM web service and get the results back in xml. Then I parse the xml to extract the data that I am interested in, in this case a list of account names.  Finally I write the account names on the html page that i am calling the web service from. The sample is written for a standalone html page but you can easily cut and paste the code into Onload, OnSave or OnChange event of any CRM forms.


Make sure you add your server name where it says below.


<html>


<head>


<meta http-equiv=”Content-Type” content=”text/html; charset=windows-1252″>


<title>Access CRM Web Services</title>


<SCRIPT language=”JavaScript”>


//Call CRM web services directly


function AccessCRMWebServices()


{


var serverUrl = “http://<ADD your Server name here>/mscrmservices/2006″;



var xmlhttp = new ActiveXObject(“Microsoft.XMLHTTP”);



alert(serverUrl+ “/crmservice.asmx”);



xmlhttp.open(“POST”, serverUrl + “/crmservice.asmx”, false);



xmlhttp.setRequestHeader(“Content-Type”, “text/xml; charset=utf-8”)



xmlhttp.setRequestHeader(“SOAPAction”, “http://schemas.microsoft.com/crm/2006/WebServices/RetrieveMultiple“)



xmlhttp.send(“<?xml version=’1.0′ encoding=’utf-8′?>”+”\n\n”+”<soap:Envelope”+



‘ xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/”‘+



‘ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”‘+



‘ xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>’+



‘ <soap:Body>’ +



‘ <query xmlns:q1=”http://schemas.microsoft.com/crm/2006/Query” xsi:type=”q1:QueryByAttribute” xmlns=”http://schemas.microsoft.com/crm/2006/WebServices”>’+



‘ <q1:EntityName>account</q1:EntityName>’+



‘ <q1:ColumnSet xsi:type=”q1:ColumnSet”>’+



‘ <q1:Attributes>’+



‘ <q1:Attribute>name</q1:Attribute>’+



‘ <q1:Attribute>address1_stateorprovince</q1:Attribute>’+



‘ </q1:Attributes>’+



‘ </q1:ColumnSet>’+



‘ <q1:Attributes>’+



‘ <q1:Attribute>address1_stateorprovince</q1:Attribute>’+



‘ </q1:Attributes>’+



‘ <q1:Values>’+



‘ <q1:Value xsi:type=”xsd:string”>WA</q1:Value>’+



‘ </q1:Values>’+



‘ </query>’+



‘ </soap:Body>’+



‘ </soap:Envelope>’)



var result = xmlhttp.responseXML.xml;



//Separate the BusinessEntities XML tag



var BEs= result.split(“<BusinessEntities>”);



//Separate the BusinessEntity XML tag



var BE = BEs[1].split(“<BusinessEntity”);



//Walk through each Business Entity tag and extract account names



for (i = 0; i < BE.length; i++)



{



first = BE[i].indexOf(“<name>”)+6;



second = BE[i].indexOf(“</name>”);



document.writeln(BE[i].substring(first,second) + “<BR>”);



}



}



//Simply call the method when the page is loaded



AccessCRMWebServices() ;


</SCRIPT>


</head>


<body>


</body>


</html>


Comments (20)

  1. Anonymous says:

    So, will this work if I am on the laptop client and off line?  Can I still tap into the CRM Web Service or do I need to query my offline data directly?

  2. Arashs says:

    Josh
    This will only work against the CRM server.  There is no offline support in V3.0 (My bad in previous comment).

  3. Darin says:

    I am trying to do the same thing with the GrantAccess method.  Not having luck with the SOAP part.  Any advice on how I can find out how to do this?

    I am not sure how to include the PrincipalAccess information (i.e. AccessMask, prinicpal) and Target into the SOAP envelop.

    Any suggestions?

  4. Anonymous says:

    We’ve copied this code (from the line that reads "var server url=…" to the closing bracket for the for loop) for testing purposes into an OnLoad event into a entity. We modifed the address1_stateorprovince field of an account to "WA" so it would return that account, but it does not. The responseXML is empty, so then the code obviously fails when trying to access a null array element (BEs[1]).

    Any idea what might be causing this?

  5. Arashs says:

    Hi Darin

    If you make a GrantAccess (or any other request) call while tracing is on, you can see the SOAP message in the trace file.  For GrantAccess, you will see this SOAP message:

    SOAP Request:

    URL:CrmService.asmx

    HttpHeader:SOAPAction: http://schemas.microsoft.com/crm/2006/WebServices/Execute

    SOAP Xml:

    <?xml version="1.0" encoding="utf-8"?>

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/&quot; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;

    <soap:Body>

    <Request xsi:type="GrantAccessRequest" xmlns="http://schemas.microsoft.com/crm/2006/WebServices"&gt;

    <Target xsi:type="TargetOwnedAccount">

    <EntityId>948c2aa0-984c-4439-a92a-c015b32dcba9</EntityId>

    </Target>

    <PrincipalAccess>

    <Principal xmlns="http://schemas.microsoft.com/crm/2006/CoreTypes"&gt;

    <PrincipalId>6ce4df0c-0948-da11-a7a7-00065ba4662d</PrincipalId>

    <Type>User</Type>

    </Principal>

    <AccessMask xmlns="http://schemas.microsoft.com/crm/2006/CoreTypes"&gt;WriteAccess</AccessMask>

    </PrincipalAccess>

    </Request>

    </soap:Body>

    </soap:Envelope>

    Now all you need to do is to create a string with this soap message like what i did with QueryByAttribute and send it to the server.

    Good luck

  6. Arashs says:

    Hi Kyle

    There might be a number of possible reasons and debugging tips 1) Make sure that you have included your server name where it says so in the code 2) Make sure activeX is enabled in your browser(go to IE -> Settings -> check the box with prompt for Activex) 3) if you are sure the browser is calling the server, make sure if the server is receiving the web service call by turning on the tracing and viewing the log to ensure the SOAP call is recieved.

  7. Anonymous says:

    Thanks for this awesome code.  I was able to modify it to create a picklist of contact names for the related account on an opportunity form and thus able to have multiple contacts, ie-client contact, client executive contact, etc on the opportunity form and views.

    I was also able to use this method to have multiple users on a form as well (main owner, technical contact, project leader, etc)

    My question is this, where do you find the XML schema structure to structure the query request in XML format?  

    I understand the “querybyattribute” structure, but what if I want to do something similar using “querybyexpression”?  I can do this in a ASPX page using C# but this would run faster if I could do this in an onchange like above.  I can’t seem to locate this in the SDK, or am I missing something really obvious?  I am just unclear of where to put/embed things like <linkedentity> and <filterexpression>, etc

    Thanks,

    Nick

  8. Arashs says:

    Nick

    Great to hear you are using this.  Two ways to find out about the schema and build your XML 1) use the CrmService.asmx web service WSDL,  the WSDL includes the schema for QueryExpression and all of the rest of CRM types 2) Enable tracing (follow the link in the my post), make a web service call using C# and view the tracing log.  The tracing log will have the SOAP XML that you need to use in your form JScript.

  9. Anonymous says:

    We’ve tried this code in the CRM Web Browser client and it works fine. The Outlook client, however, fails when it reaches the “xmlhttp.open()” line. The error we receive is an “Unknown name” error.

    Any ideas what may be causing this?

    Thanks,

    – Kyle

  10. Anonymous says:

    Webservices Webservices Webservices….alla pratar om dem, alla vill ha det och vi har det 🙂 Visste

  11. Anonymous says:

    Or you can use Microsofts own JavaScript classes (or modified versions of them) like I did here: http://www.sharepointdemo.biz/blogs/bjarne/default.aspx?BlogId=101

  12. Darin says:

    Nick,

        Another way, one that seemed easier than the trace log, was to use a tool called WebserviceStudio.  Just point to your webservice (ie. CrmService.asmx) and press a button to get all the available method in the web service.

    Then you can choose between a QueryExpression or QueryByAttribute, etc.  Then just fill in your criteria and press another button.  On the Request/Response tab will be your SOAP messages, just how you need them.

    Took me an hour to figure out how to use it correctly to generate the correct SOAP messgae (if you set it up wrong, you get an error).

    Hope this app helps you, too.

    Darin

  13. Anonymous says:

    The first time the web-service is called after opening CRM, I’m being prompted with a Login.  Is there any way to eliminate this?

    Thanks

    Ken

  14. KyleCRM says:

    We’ve been using this code for quite some time and it works great. However, if we’re using the Outlook Laptop Client, and we’re trying to access "http://localhost:2525/MSCRMServices/2006&quot; (the local website), the process fails.

    Is there a way to get this to work?

    Thanks,

    • Kyle
  15. Anonymous says:

    Solution for unknown name

    The problem is, that your webservice is running on a separate website. It

    should be working fine, if you create a new virtual-directory under the Crm

    Website and try it again. (Don’t forget to change the link on your formscript

    to the new WS-Url 😉

    If anybody is good in security topics, why does this happen and is it

    possible to run the webservice on a separated website?

    Hope anybody can save the time I wasted…

  16. Arashs says:

    IE prevents calling web services that are in different domain from where the page is loaded from.  This is a security feature that prevents cross-site scripting.   The Outlook client uses the current machine hence it is on a seperate domain.  The suggestion is to  have your web service and web site both on the same domain.

  17. Anonymous says:

    There are some good reasons why an appointment is read-only after closing it. But sometimes it could

  18. Anonymous says:

    Great site! Good luck to it’s owner!

  19. Anonymous says:

    Hi I have a script similar to above that uses location.hostname to work out part of the connection for xmlHttpRequest.Open

    For those that use the Outlook Off Line Client it fails to run in all circumstances (both online and off line).  Change the location.hostname to the server location and it works in the outlook off line client when you are online and connected to the network, but fails as soon as you are disconnected from the network (kind of obvious) Note: using version 3.053.

    I am wondering if there is something that I can do on the local websever (Cassini I think) that is on the client machine that has CRM Outlook Off Line Client is installed.  A couple of posts referred to using local virtual directory (Peter Widmer) but when looking for the cassini exe file on the local machine it was not there.

    Help Please 🙂