{KnowHow} How to use Discovery Service Web API of Dynamics CRM 2016

Recently I posted in my blog on how to execute Web API queries from external ASP.NET web application to retrieve data. Details could be found here – https://debajmecrm.com/knowhow-how-to-execute-web-api-calls-to-microsoft-dynamics-crm-from-an-external-asp-net-web-application/.

After this post, people has been asking me to how to leverage the Discovery service Web API with Dynamics CRM 2016. And the most common question being – “Why am I not getting any response when I query for instance data through the Web API?”

Well, let’s jump to an example here. The following is the code for a blog reader sent me. In a web-resource on a form, he simply invoked the webapi endpoint of the discovery service to get the instance details.

function getOrgs() {
    var req = new XMLHttpRequest();
    debugger;
    req.open("GET", “https://disco.crm.dynamics.com/api/discovery/v8.0/Instances”);

    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 == 200) {
                var discovery = JSON.parse(this.response);
                alert("User Id : " + discovery.Url);
            }
            else {
                var error = JSON.parse(this.response).error;
                alert(error.message);
            }
        }
    };
    req.send();
}

He informed and told that he is getting empty response and also not getting an error.  I took his code and when I inspected through fiddler, I found the following error at the connection level

“Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://xrmtr14.crm.dynamics.com’ is therefore not allowed access”

https://xrmtr14.crm.dynamics.com is my CRM online instance here .

So what is ‘Access-Control-Allow-Origin’ header?

Access-Control-Allow-Origin is a CORS (Cross-Origin Resource Sharing) header.

When Site A tries to fetch content from Site B, Site B can send an Access-Control-Allow-Origin response header to tell the browser that the content of this page is accessible to certain origins. (An origin is a domain, plus a scheme and port number.) By default, Site B’s pages are not accessible to any other origin; using the Access-Control-Allow-Origin header opens a door for cross-origin access by specific requesting origins.

So it’s clear that if you want to access the discovery service web api endpoint from within CRM, the discovery service endpoint would not allow you to do the same since Access-Control-Allow-Origin header is not present in the server response. Also if your really ask me, it makes sense as well. After all why do we need to query the instance in which we are already in.

The question now is why then how can we use the discovery service web Api. If you are well versed with the concept of OAuth, let me tell you the discovery service web api endpoints can also be accessed through OAuth 2.0.

So we will connect to discovery web API endpoint through asp.net web application. Please follow the link – https://debajmecrm.com/knowhow-how-to-execute-web-api-calls-to-microsoft-dynamics-crm-from-an-external-asp-net-web-application/ as I am going to use the same example for this. The only difference is the code to access the instances.

So all the steps are the same except for the fact that I have added a new button called ‘Get Instance Details’ which when clicked would fetch me the instance details. Below is the code that I wrote in the button event Handler.

 

if (Session["AuthResult"] != null)
            {
                var authResult = (AuthenticationResult)Session["AuthResult"];
                var webRequest = (HttpWebRequest)WebRequest.Create(new Uri("https://disco.crm.dynamics.com/api/discovery/v8.0/Instances”));

                webRequest.Method = "GET";
                webRequest.ContentLength = 0;
                webRequest.Headers.Add("Authorization", String.Format("Bearer {0}", authResult.AccessToken));
                webRequest.Headers.Add("OData-MaxVersion", "4.0");
                webRequest.Headers.Add("OData-Version", "4.0");
                webRequest.ContentType = "application/json; charset=utf-8";

                using (var
response = webRequest.GetResponse() as System.Net.HttpWebResponse)
                {
                    //Get reader from response stream
                    using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
                    {
                        var instances = new List<Instance>();
                        string responseContent = reader.ReadToEnd();

                        dynamic dynamicObj = JsonConvert.DeserializeObject(responseContent);

                        foreach (var data in dynamicObj.value)
                        {
                            var instance = new Instance
                            {
                                Url = data.Url,
                                Id = data.Id,
                                FriendlyName = data.FriendlyName,
                                UniqueName = data.UniqueName
                            };

                            instances.Add(instance);
                        }

                        InstanceGrid.DataSource = instances;
                        InstanceGrid.DataBind();
                    }
                }
            }

 

All I have done in the above code block is change the URL to point to the discovery service web API.

 

And this is final screenshot

image

 

Hope this helps!