Monday, July 16, 2012

CRM2011 and Cross Domain calls Part 1

What are cross domain calls?

These are the calls we make from a web page using JavaScript to access resources at a different URL. This URL can be separate website or domain or port name. This can be calling a same URL using ip address instead of using computer name as I discussed in my previous post.
In IE, these calls are not allowed. If you try to call a web service using ajax, you will get an error message “Access is denied”. The problem is XMLHttpRequest object used in the AJAX calls. This object stops us making calls to other domains. AJAX communication is restricted to the server of origin. It is by design and it protects as against the malicious hack these calls can cause. But sometimes these calls are essential. In day to day basis we do need to make these calls.  For example, if you would like display exchange rates in CRM2011, we have to make a call to some external API/web service. In this blog we will discuss how to make cross domain calls in JavaScript.
I found this free currency exchange API http://openexchangerates.org/api/latest.json. This URL will return the exchange rates for about 150 currencies in JSON format. I have created an html web resource in CRM2011 to retrieve the exchange rates using JQuery AJAX call. Here is the code. I have also added a reference to JQuery web resource to this html web resource. 
<HTML><HEAD><TITLE></TITLE>
<SCRIPT src="https://crm5org01a03.crm5.dynamics.com//WebResources/new_/jquery1.7.2.js"></SCRIPT>

<SCRIPT type=text/javascript>

function callExchangeRate()
{

//debugger;
jQuery.support.cors = true;
var myurl = "http://openexchangerates.org/api/latest.json";
   $.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: myurl,
        beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
        success: function (data, textStatus, XmlHttpRequest) {
            // Use for a single selected entity
            alert(data.rates.AUD); // this line will display the exchange rate of AUD aganst USD
        },
        error: function (xmlHttpRequest, textStatus, errorThrown) {
            alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
        }
    });

}

</SCRIPT>

<META charset=utf-8></HEAD>
<BODY onload=callExchangeRate() contentEditable=true>Testing Cross Domain Calls</BODY></HTML>


When we run this web resource, we will receive the following error message.image
image

How to make a cross domain calls

So how can we get around this problem? There are few solutions to this problem. I will discuss the following 2 options.
  1. JSONP
  2. XDomainRequest (Cross Domain Request)

Using JSONP

JSONP stands for JSON with padding but it has nothing to do with JSON. This is an old technique of using <script> tag to load external resources. JSONP dynamically creates a <script> tag to get the data instead of using XMLHttpRequest object. The <script> tag will load whatever is returned from the URL specified as its “src” attribute. The other cool feature of JSONP calls is Callback function.
For example look at the following URL
http://openexchangerates.org/api/latest.json. If we use this URL in <script> tag it will attached the response to “src” tag. But if we use http://openexchangerates.org/api/latest.json?callback=parseRequest, the response will be wrapped in a parseRequest  method. We can use this method to process the response.
I have created an html web resource using this technique. Here is the code.
<HTML><HEAD><TITLE></TITLE>
<SCRIPT type=text/javascript>
function callTheJsonp()
{
//debugger;

// the url of the script where we send the asynchronous call 

var url = "http://openexchangerates.org/api/latest.json?callback=parseRequest";

// create a new script element

var script = document.createElement('script');

// set the src attribute to that url

script.setAttribute('src', url);

// insert the script in out page

document.getElementsByTagName('head')[0].appendChild(script);

}

// this function should parse responses.. you can do anything you need.. 

// you can make it general so it would parse all the responses the page receives based on a response field

function parseRequest(data)
{
   try // try to output this to the javascript console 
   {
       alert("AUD: " + data.rates.AUD); 
   }
   catch(an_exception)
   // alert for the users that don't have a javascript console
   {
      alert(data); 
   } 

} </SCRIPT>

<META charset=utf-8></HEAD>
<BODY onload=callTheJsonp() contentEditable=true>Testing Cross Domain Calls</BODY></HTML>

When we open the html web resource, it will display the following message.
image
Note: To work with JSONP, the web service has to be JSONP compatible. If you are creating your own web services and you would like to consume them in JavaScript using JSONP, make sure they are  JSONP compatible. The currency conversion API, I am using in this example supports JSONP so does flicker, twitter and Google APIs etc.
In my next blog I will explain how to make JSONP calls using JQuery and AJAX and also how to use XDomainRequest.

5 comments: