Monday, February 16, 2009

Cross Domain jQuery AJAX Call

In AJAX and jQuery, I talked about how to use jQuery and AJAX to dynamically get data from a separate page. But that example has a limitation: the AJAX call has to be in the same domain of initial page, otherwise the AJAX requests are characterized as Cross-domain Request ("XDR"), and all popular web browsers will deny those cross domain requests due to security consideration. For example, a JavaScript (AJAX call) in a page inside domain abc.com won't be able to talk with bcd.com because abc.com and bcd.com are in different domains.

However there're some workarounds for such issue. jQuery uses the JSONP approach. Following example shows how to dynamically populate weather information from a different domain. The JavaScript and the page content is below:
<script language="javascript">
/* Sample of JSON Return:
({ "Location":{"Code":"CityCode_1141","Name":"HFLaJlrf"},
"WeatherInfoList":[
{"Day":"Mon","Image":"Images\/5.gif","Temperature":"32°\/22°"},
{"Day":"Tue","Image":"Images\/28.gif","Temperature":"20°\/-8°"},
{"Day":"Wed","Image":"Images\/9.gif","Temperature":"7°\/13°"},
{"Day":"Thu","Image":"Images\/5.gif","Temperature":"11°\/-6°"}
]})
*/

/* Dynamically load weather info using cross domain Ajax call with JSON */
/* Refer to: http://docs.jquery.com/Release:jQuery_1.2/Ajax#Cross-Domain_getJSON_.28using_JSONP.29
http://www.theurer.cc/blog/2005/12/15/web-services-json-dump-your-proxy/
*/

function getWeatherJSON() {
var server = "http://myserver/WeatherService.aspx"; // XDR request
var weatherServiceUrl = server + "?location=" + weatherLocation + "&format=json&jsoncallback=?";
$.getJSON(weatherServiceUrl, function(data) {
$("#WeatherTitle").html(data.Location.Name + " Code(" + data.Location.Code + ")");
/* Loop through all items and set weather data*/
$.each(data.WeatherInfoList, function(i, item) {
$("#Weather_Day" + i + "_Day").html(item.Day);
$("#Weather_Day" + i + "_Image").attr("src", item.Image);
$("#Weather_Day" + i + "_Temperature").html(item.Temperature);
});
$("#divWeatherControl").show();
$("#divWeatherLoading").hide();
});
}
</script>

<div id="divWeatherControl" style="border: solid 1px blue; text-align: center; display: none;">
<table>
<tr align="center">
<td colspan="4"><p id="WeatherTitle" /></td>
</tr>
<tr align="center">
<td><p id="Weather_Day0_Day" /></td>
<td><p id="Weather_Day1_Day" /></td>
<td><p id="Weather_Day2_Day" /></td>
<td><p id="Weather_Day3_Day" /></td>
</tr>
<tr align="center">
<td><img id="Weather_Day0_Image" /></td>
<td><img id="Weather_Day1_Image" /></td>
<td><img id="Weather_Day2_Image" /></td>
<td><img id="Weather_Day3_Image" /></td>
</tr>
<tr align="center">
<td><p id="Weather_Day0_Temperature" /></td>
<td><p id="Weather_Day1_Temperature" /></td>
<td><p id="Weather_Day2_Temperature" /></td>
<td><p id="Weather_Day3_Temperature" /></td>
</tr>
</table>
</div>
<div id="divWeatherLoading" style="border: solid 1px blue; text-align: center;">
Loading weather data...
</div>
Note myserver/weatherservice.aspx can be any domain. The code behind of the weatherservice.aspx page (empty html):
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;

public partial class WeatherService : System.Web.UI.Page
{
private readonly string WEATHERLOCACTIONNAME = "location";
private readonly string CALLBACKFUNCNAME = "jsoncallback";

protected void Page_Load(object sender, EventArgs e)
{
string html = RenderJSON();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Write(html);
Response.End();
}

private string RenderJSON()
{
string location = Request.QueryString[WEATHERLOCACTIONNAME];
string callBackFunction = Request.QueryString[CALLBACKFUNCNAME];
if (!string.IsNullOrEmpty(location) && !string.IsNullOrEmpty(callBackFunction))
{
WeatherInfo wloc = BLL.GetWeatherForecast(location);
if (wloc == null) return string.Empty;
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(wloc.GetType());
StringBuilder sb = new StringBuilder();
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, wloc);
sb.Append(callBackFunction + "(");
sb.Append(Encoding.UTF8.GetString(ms.ToArray()));
sb.Append(")");
}
return sb.ToString();
}
return string.Empty;
}
}
The result looks like: