ObservableDeveloper - Leonid Sorokin's Blog

.NET, Silverlight, ASP.NET, Web Services, SQL Server, Cloud Computing, and much more...

About the author

I am a .NET Software Developer, Consultant, and Trainer from Toronto, Canada specializing in web development with Rich Internet Applications on the Microsoft development tool chain.

My Photo

Microsoft Certified Professional Developer

Solving cross domain issues with Silverlight 2

We all know that Silverlight and Flash follow a similar security model and run in the browser sandbox. These RIA platforms essentially request a cross domain policy file when a request is sent from the RIA application to a web service. If the policy file is not present or does not list your domain as an allowed domain then you have three choices:

  • Call, write, and ask the company/person to list your domain (i.e. the domain from which your Silverlight application is downloaded) as an allowed domain in the client policy file (clientaccesspolicy.xml or crossdomain.xml) – may not be very reasonable
  • Create your own proxy – the proxy will sit on your domain, and will make calls to the external domain. Hence, your Silverlight app will call the proxy on the same domain, so the Silverlight runtime will avoid asking for the cross domain policy file. Your proxy will simply call the external service and pass the response back to the Silverlight application
  • Use a 3rd party proxy service instead of rolling your own – such as Yahoo Pipes @ http://pipes.yahoo.com/pipes or the Google AJAX Feed API @ http://code.google.com/apis/ajaxfeeds
  • Trick the browser and avoid using any proxy whatsoever. This only works if the Silverlight application can access the Html Bridge and inject a JavaScript element into the head tag of the page. Also, you need to understand how to pass the response into a Silverlight control. If you don’t know how this can be accomplished then this approach will not work for you. This is the option I will discuss further, since the other ones are self explanatory

I am not sure if I would recommend using such an approach because it feels like a hack, but it does indeed work. This approach may not work at all, if, for example, you are loading your Silverlight application XAP file from a different domain than the web page that is hosting the Silverlight content. I am not sure if the Silverlight control will be able to modify the head Html element of the host page.

Here is the most important snippet of code you need in order to implement this trick:

HtmlElement head = HtmlPage.Document.GetElementsByTagName("head")[0] as HtmlElement;
HtmlElement javascriptContent = HtmlPage.Document.CreateElement("script"); 
javascriptContent.SetProperty("type", "text/javascript"); 
javascriptContent .SetProperty("src", "http://aWebServiceSomeWhere"); 
head.AppendChild(javascriptContent);

 

Do you see the trick? The browser’s javascript engine will say, “oh, look, we have a new script element that has been added to the header, and it has a URL as the src property – I need to download and then execute that javascript!” Since the request is made from the browser’s DOM and has no relevance to any RIA technology, all outbound requests will succeed an no cross domain policy file is requested. After the javascript is returned, it is necessary to pass the results back to the Silverlight control. Clearly, the only reasonable way to do it is to force the javascript to contain a method call into your Silverlight control. For instance, Twitter allows you to specify a callback=YourJavascriptFunction in the request, and the resulting JavaScript returned from the Twitter service will contain YourJavascriptFunction in the response.

Bryant Likes wrote a Silverlight Twitter control that demonstrates using this trick, and a fall-back mechanism that uses Yahoo Pipes. Have a look @ http://blogs.sqlxml.org/bryantlikes/archive/2009/01/23/twilight-a-silverlight-twitter-badge.aspx and http://www.codeplex.com/Twilight

By the way, I am currently using Bryant’s excellent Silverlight Twitter badge on my blog. 


Permalink | Comments (0) | Post RSSRSS comment feed

How to pass arbitrary data in a Message object using WCF

I was searching all over the Internet, and yet I could not find any information about this seemingly simple task. Suppose you have an arbitrary string that you would like to return from a WCF (Windows Communication Foundation) service.

You can do one of the following on the service contract:

  1. Create a method/operation with a string return type
  2. Create a method/operation with a Stream return type in order to have full control over how serialization is performed. You can find a good explanation @ http://blogs.msdn.com/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-web.aspx 

However, you may find yourself in a situation where you cannot return a Stream type because you are sending the response somewhere deep inside the WCF stack and you only have the System.ServiceModel.Message type to work with. Here is the documentation of the Message class I am talking about - http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.message.aspx

Aside: If you have ever worked with RIA (Rich Internet Applications) such as Flash or Silverlight you will most likely be familiar with cross-domain policy files, such as crossdomain.xml (Flash) or clientaccesspolicy.xml (Silverlight). I am not going to go into why they are needed, but needless to say, I had to return one of these files depending on the request made from the client. Both of these files contain perfectly valid XML.

If you look carefully at the Message.CreateMessage method you will see it has many overloads. At first, I tried this:

Message reply = Message.CreateMessage(MessageVersion.None, "", XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes("[XML goes here]"))));

This works fine as long as the XML you are passing to XmlReader does not contain DTD definitions. For some security reason having a DTD in the XML causes an exception on the XmlReader.Create call. I was able to avoid an exception by setting the ProhibitDtd property on XmlReaderSettings to false, but WCF decided I was performing evil deeds, and independently decided to emit my XML without the DTD definition.

If you look at the overloads of Message.CreateMessage you will quickly realize that you must serialize as XML. In my case, the DTD in the crossdomain.xml caused the headache, but what if you want to pass an arbitrary string such as "Hello World" back from the service as-is, what do you do then? Anything you may try will cause the default DataContractSerializer to kick in and serialize your object. The catch is, you do not actually want serialization.

Here is the code that you need in order to return arbitrary text using WCF's Message class.

/// <summary>
/// Necessary to write out the contents as text (used with the Raw return type)
/// </summary>
public class TextBodyWriter : BodyWriter
{
        byte[] messageBytes;

        public TextBodyWriter(string message)
            : base(true)
        {
            this.messageBytes = Encoding.UTF8.GetBytes(message);
        }

        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            writer.WriteStartElement("Binary");
            writer.WriteBase64(this.messageBytes, 0, this.messageBytes.Length);
            writer.WriteEndElement();
        }
}

// ....

// Here is sample code that does the magic of returning raw text to the client...
string response = "Hello World!";
Message reply = Message.CreateMessage(MessageVersion.None, null, new TextBodyWriter(response));
reply.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(WebContentFormat.Raw); // very important - forces WCF to serialize the message body contents as "Raw" which effectively means no serialization
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain"; // can also be "application/xml" or anything you prefer


Permalink | Comments (0) | Post RSSRSS comment feed