One of the easiest ways to deal with cross-domain issues in Silverlight is to write a WCF service that acts as a proxy for the calls you want to make. For example, in my Twitter control, I want to be able to load XML from the RESTful Twitter service at the following address:
http://twitter.com/statuses/user_timeline/wkh.xml
However, when invoking the RESTful Twitter service from a WebClient in Silverlight, I get the dreaded "Download Failure" error when I call DownloadStringAsync. This is because the Twitter.com site doesn't have an entry in its clientaccesspolicy.xml file for my test domain. And that's never going to happen, right? To solve this problem, consider exposing a WCF service from the site from which the Silverlight control emanated. The WebClient class can be used on the server side without the same cross-domain control being applied by Twitter.com. For the example outlined above, I called my service TwitterProxy. The ITwitterProxy couldn't be simpler:
using System.ServiceModel;
[ServiceContract( Namespace = "urn:gotnet:biz:Services:2008:05" )]
public interface ITwitterProxy
{
[OperationContract]
string GetUserTimeline( string user, int count );
}
The service implementation is also quite simple. It uses the WebClient just as I would from Silverlight (if I could):
using System;
using System.Net;
public class TwitterProxy : ITwitterProxy
{
public string GetUserTimeline(string user, int count)
{
user = (user != null) ? user.Trim() : String.Empty;
count = (count < 1) ? 1 : (count > 20) ? 20 : count;
WebClient client = new WebClient();
Uri uri = new Uri( String.Format("http://twitter.com/statuses" +
"/user_timeline/{0}.xml?count={1}", user, count ) );
return client.DownloadString(uri);
}
}
Expose an SVC file on the server to host the new service. In this example, the URL to invoke the new service is:
http://localhost/WebSite/TwitterProxy.svc
So, to invoke this service from Silverlight, add a proxy to the Silverlight control, using the service address and invoke it like this:
private void OnLayoutRootLoaded(object sender, RoutedEventArgs e)
{
TwitterProxyClient client = new TwitterProxyClient(
new BasicHttpBinding(), new EndpointAddress(
"http://localhost/Website/TwitterProxy.svc"));
client.GetUserTimelineCompleted += OnGetUserTimelineCompleted;
client.GetUserTimelineAsync("wkh", 10);
}
void OnGetUserTimelineCompleted(object sender,
GetUserTimelineCompletedEventArgs e)
{
// open a Twitter Status dialog, passing the XML string
NavigationHelper.Navigate(new FadeTransition(
TimeSpan.FromMilliseconds(750.0d)), new Status(e.Result));
}
The dreaded "Download failure" error is gone. Nice. of course, the potentially bad side effect of this technique is that all of the Twitter service traffic will be flowing through the server that's hosting the TwitterProxy. Think about that before using this technique.