Enabling cross-domain calls for Silverlight apps on self-hosted web services
In order for a Silverlight (or Flash) app coming from one domain to be able to consume data from services in a different domain, the service must "allow" the app to do so by providing a policy file which grants access (to prevent all sorts of cross-site scripting attacks). This policy file must be located in the root of the "domain" (hostname + port), so if your service is located at https://my.service.com:8000/Service/CoolService.svc/Endpoint, the policy file must be located at https://my.service.com:8000/ClientAccessPolicy.xml (or https://my.service.com:8000/crossdomain.xml in case of the Flash format). That's fairly easy to do on a IIS-hosted service (simply put the static policy file in the root of the web), but for self-hosted apps it isn't as simple (there's no "root" of the web).
To solve this problem for self-hosted WCF services, you can use the web programming model support fairly easily. Basically, you'd define the base address at the root of the domain, and have a web endpoint at the "" address. All the "real" service endpoints would then be in different addresses. The example below shows it in action:
[update (2010/07/24) : if your self-hosted service uses the TCP binding (new in SL4), you can look at the solution at my new post at https://blogs.msdn.com/b/carlosfigueira/archive/2010/07/25/enabling-cross-domain-calls-for-sl-apps-on-self-hosted-tcp-services.aspx]
A complete VS solution with a SL application and the self-hosted service can be found at the MSDN Code Gallery at https://code.msdn.microsoft.com/Accessing-self-hosted-WCF-7872c931. The code for the service itself is listed below.
- public class SelfHostedServiceWithSilverlightPolicy
- {
- [ServiceContract]
- public interface ITest
- {
- [OperationContract]
- string Echo(string text);
- }
- [ServiceContract]
- public interface IPolicyRetriever
- {
- [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
- Stream GetSilverlightPolicy();
- [OperationContract, WebGet(UriTemplate = "/crossdomain.xml")]
- Stream GetFlashPolicy();
- }
- public class Service : ITest, IPolicyRetriever
- {
- public string Echo(string text) { return text; }
- Stream StringToStream(string result)
- {
- WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
- return new MemoryStream(Encoding.UTF8.GetBytes(result));
- }
- public Stream GetSilverlightPolicy()
- {
- string result = @"<?xml version=""1.0"" encoding=""utf-8""?>
- <access-policy>
- <cross-domain-access>
- <policy>
- <allow-from http-request-headers=""*"">
- <domain uri=""*""/>
- </allow-from>
- <grant-to>
- <resource path=""/"" include-subpaths=""true""/>
- </grant-to>
- </policy>
- </cross-domain-access>
- </access-policy>";
- return StringToStream(result);
- }
- public Stream GetFlashPolicy()
- {
- string result = @"<?xml version=""1.0""?>
- <!DOCTYPE cross-domain-policy SYSTEM ""https://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"">
- <cross-domain-policy>
- <allow-access-from domain=""*"" />
- </cross-domain-policy>";
- return StringToStream(result);
- }
- }
- public static void Main()
- {
- string baseAddress = "https://" + Environment.MachineName + ":8000";
- ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
- host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "basic");
- host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
- ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
- smb.HttpGetEnabled = true;
- host.Description.Behaviors.Add(smb);
- host.Open();
- Console.WriteLine("Host opened");
- Console.WriteLine("Press ENTER to close");
- Console.ReadLine();
- host.Close();
- }
- }