Silverlight allows you to make web service calls to web services that are a part of the same website with no restrictions, but by default does not allow cross-domain calls. In order for a Silverlight 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. When calling a cross-domain service, Silverlight will check the existence of the ClientAccessPolicy.xml file. This is a format defined by Silverlight and provides a way to define who can access what services. If it is not found, it will then default to look for the CrossDomain.xml policy file, which is the default file implemented for Adobe Flash.
Note: ClientAccessPolicy.xml is more feature rich, but CrossDomain.xml should be used if you want to give access to both Silverlight and Flash applications in one step. It is not necessary to have both files together.
Silverlight 3 currently supports the following WCF Service bindings:
1) customBinding
2) basicHttpBinding
3) pollingDuplexHttpBinding
Policy File Location
The policy file must be located at the root of the “domain” (hostname + port), so if your service is located at http://my.service.com:8000/Service/SomeService.svc/Endpoint, the policy file must be located at http://my.service.com:8000/ClientAccessPolicy.xml.
For IIS-hosted service, simply put the static policy files (ClientAccessPolicy.xml and/or CrossDomain.xml) in the root directory of the service (for example, in the c:\inetpub\wwwroot directory of an IIS web server).
For Self-hosted WCF service, you will have to have your WCF service host serve up the policy file on demand. Here’s an example:
1) First, create a service contract that can serve up the XML content:
[ServiceContract]
public interface IClientAccessPolicy
{
[OperationContract]
[WebGet(UriTemplate = "/ClientAccessPolicy.xml")]
XElement GetClientAccessPolicy();
}
2) Secondly, create a base service class to implement the IClientAccessPolicy interface like so:
public class BaseWebService: IClientAccessPolicy
{
public XElement GetClientAccessPolicy()
{
return new XElement("access-policy",
new XElement("cross-domain-access",
new XElement("policy",
new XElement("allow-from",
new XAttribute("http-request-headers", "*"),
new XElement("domain",
new XAttribute("uri", "*"))),
new XElement("grant-to",
new XElement("resource",
new XAttribute("path", "/"),
new XAttribute("include-subpaths", "true"))))));
}
}
3) Lastly, on the host application:
Uri[] addresses = new Uri[] { "www.mydomain.com" };
var host = new ServiceHost(typeof(SomeService), addresses);
var endpoint = host.AddServiceEndpoint(typeof(IClientAccessPolicy),
new WebHttpBinding(), string.Empty);
endpoint.Behaviors.Add(new WebHttpBehavior());
var smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
Policy Files
1) ClientAccessPolicy.xml (unlimited cross site access)
<?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 include-subpaths="true" path="/"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
You can limit access to the service for a specific domain by providing a URI value like so:
<allow-from http-request-headers="*">
<domain uri="http://www.somedomainalpha.com" />
<domain uri="http://www. somedomainbeta.com" />
</allow-from>
You can use wildcards in the domain names to allow sub-domains. E.g., *.somedomain.com allows requests from mail.somedomain.com, admin.somedomain.com, and so on. You can also limit access to a specific service on the domain by specifying the resource path as follows:
<grant-to>
<resource path="/service/" include-subpaths="true"/>
</grant-to>
2) CrossDomain.xml
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>