How to make cross-site service calls in Silverlight using JSON
Share
You’re quite familiar by now with the need for cross-domain policy files and service proxies if you’ve spent any time writing applications in Silverlight that behave like mashups. The root of the cross-domain issue is in how browsers restrict XML requests, and one emerging and popular way to circumvent this problem is to make JSON calls instead, which are cross-site friendly.
Silverlight has a nice set of browser interoperability classes waiting to be used for these kinds of purposes. As a quick and dirty example let’s create some extensions that allow us to make JSON calls to grab data from Twitter.
The first thing we’ll do is create an event class that manages incoming JSON data received from a service call. We’ll want to work with objects rather than a string, so I’ve borrowed some code from a conversation on StackOverflow to provide a simple conversion from a JSON string to a dictionary of property names matched to objects. You can use whatever JSON converter you like, or download my version.
JsonEvent.cs
[ScriptableType]
public class JsonEvent
{
public static event EventHandler<JsonEventArgs> Responded;
public static void OnResponded(string json)
{
if(Responded != null)
{
Responded(null, new JsonEventArgs(json.ToDictionary()));
}
}
[ScriptableMember]
public void Received(string json)
{
OnResponded(json);
}
}
JsonEventArgs.cs
public class JsonEventArgs : EventArgs
{
public IDictionary<string, object> Data
{
get;
private set;
}
public JsonEventArgs(IDictionary<string, object> data)
{
Data = data;
}
}
Now let’s take a look at the extension method that’s going to perform the service call for us.
JsonExtensions.cs
public static class JsonExtensions
{
private static readonly Dictionary<string, object>
_scriptables = new Dictionary<string, object>();
public static void SendJson(this string url)
{
if(!_scriptables.ContainsKey("Json"))
{
HtmlPage.RegisterScriptableObject("Json", new JsonEvent());
}
var id = HtmlPage.Plugin.Id;
HtmlPage.Window.Invoke("jsonLoad", url, id);
}
}
In the snippet above, we’ve registered a new instance of our JsonEvent class to receive incoming data, retrieved the name of the Sys.UI.Silverlight.Control instance on our host page, and called a JavaScript method called jsonLoad(), passing it the web request URL, and the control’s ID.
On the client-side, we’ll need the usual script to handle JSON data (www.json.org/json2.js for example), as well as a few lines to make the JSON call, and add a callback to our scriptable method registered on JsonEvent.
SilverlightJson.js
var $id;
function jsonLoad(url, id)
{
$id = id;
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.src += hasParameters(url) ? '&' : '?';
script.src += 'callback=jsonCallback';
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
};
function jsonCallback(jsonData)
{
var id = $id;
var silverlight = document.getElementById(id);
if (silverlight)
{
var response = JSON.stringify(jsonData);
silverlight.Content.Json.Received(response);
}
};
function hasParameters(url)
{
url = url.replace(/[[]/, "\[").replace(/[]]/, "\]");
var pattern = "[\?]w+=([^]*)";
var regex = new RegExp(pattern);
var results = regex.exec(url);
return results != null;
}
With the script in place, making a client-side call to Twitter is easy.
public partial class Page
{
public Page()
{
InitializeComponent();
JsonEvent.Responded += JsonEvent_Responded;
"http://twitter.com/users/show/dimebrain.json".SendJson();
}
static void JsonEvent_Responded(object sender, JsonEventArgs e)
{
// Twitter response is in key-value pairs
var name = e.Data["name"];
}
}
This approach requires that the service API you’re calling supports the “callback” parameter, but the popularity and simplicity of using JSON on the client should see many of the most used APIs, like Twitter, providing this feature. Whether you stick with proxies or use this method, hopefully you have a better understanding of what is possible through the browser and Silverlight in tandem.
Socialized