How to consume a self-hosted WCF service in a cross-domain environment by Silverlight client
Background:
When a Silverlight client tries to consume WCF service, it’s allowed only site-of-origin communication by default. For example, a Silverlight control hosted at https://domainA/default.aspx can only consume only services on that same domain by default, that is: https://domainA/WCFService.svc, but not https://domainB/WCFservice.svc.
To make the WCF service hosted at : https://domainB/WCFservice.svc be accessible for Silverlight hosted at https://domainA/default.aspx, we need to configure a opt-in cross-domain policy explicitly which is called clientaccesspolicy.xml or crossdomain.xml. There are many articles addressing this topic:
Making a Service Available Across Domain Boundaries
https://msdn.microsoft.com/en-us/library/cc197955(VS.95).aspx
Warning: Could not locate cross-domain policy at '<URL>'
https://msdn.microsoft.com/en-us/library/cc838225(VS.95).aspx
When our WCF service is hosted in IIS, we only need to deploy the clientaccesspolicy.xml at the root folder where the WCF service is published. For example: if our WCF service is published as: https://domainB/WCFservice.svc, then the cross-domain policy should be accessible by https://domainB/clientaccesspolicy.xml.
However, it will be a little different for the self-hosted WCF service. In this article, we will demonstrate how to consume self-hosted WCF service in a cross-domain environment by Silverlight client. At the same time, as NET.TCP binding is supported by Silverlight4 now, the steps will be totally different for NET.TCP Binding compared to basicHttpBinding. In this article, I would like to introduce the steps for both BasicHttpBinding scenario and Net.TCP binding scenario.
How to consume a self-hosted WCF service
l Scenario 1: Self-hosted WCF service with basicHttpBinding.
Ø Step1:
Define an interface “IPolicyRetriever” :
[ServiceContract]
public interface IPolicyRetriever
{
[OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
Stream GetSilverlightPolicy();
[OperationContract, WebGet(UriTemplate = "/crossdomain.xml")]
Stream GetFlashPolicy();
}
Ø Step 2:
Implement above interface in your WCF service:
public class WCFService:IWCFService,IPolicyRetriever
{
//IWCFService implementation
…
…
//IPolicyRetriever implementation
private 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);
}
Ø Step 3:
Publish an endpoint which address is “ ” based on the host address of HTTP schema in order to accept the request for clientaccesspolicy.xml. We can achieve this by programmatically or administratively.
Programmatically:
Type serviceType = typeof(WCFService);
ServiceHost host = new ServiceHost(serviceType);
host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
Administratively:
<behaviors>
<endpointBehaviors>
<behavior name="WebHttpNewBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
...
</behaviors>
<services>
<service behaviorConfiguration="NewBehavior">
…
<endpoint behaviorConfiguration="WebHttpNewBehavior" binding="webHttpBinding"
bindingConfiguration="" name="PolicyEndpoint" contract="WCFService.IPolicyRetriever" />
...
</service>
</services>
Upon above steps, when a Silverlight control sends out a Request for cross-domain self-hosted WCF service, this server of service side should be able to retrieve its client access policy and then decide if this request is authorized.
l Scenario 2: Self-hosted WCF service with NET.TCP binding.
Although it’s declared that NET.TCP binding is supported by Silverlight 4 as follows:
WCF NET.TCP Protocol in Silverlight 4
https://www.silverlightshow.net/items/WCF-NET.TCP-Protocol-in-Silverlight-4.aspx
Before starting our steps, I’d like to highlight some points which are different with basicHttpBinding scenario.
1. Actually NET.TCP schema is not recognized by Silverlight 4. At least currently it is. In order to use NET.TCP binding, it’s needed that we use customBinding.
2. TCP Port range is limited within 4502 – 4534, which means that your WCF service must be published within above port range so that the Silverlight client can access it.
In order to make the client access policy be accessible, TCP port 943 is used to expose policy dedicatedly before Silverlight4 RC.
For more details, you can reference to below article:
Network Security Access Restrictions in Silverlight
https://msdn.microsoft.com/en-us/library/cc645032(VS.95).aspx
To make our life easier, Tomasz Janczuk from Microsoft has created a free template for console applications that serves like TCP socket policy server.
However, the net.tcp protocol in Silverlight 4 RC expects the socket policy to be available over the HTTP protocol at the root of the domain as opposed to over TCP at port 943 as before.
You can reference to below article about this change:
Pub/sub sample with WCF net.tcp protocol in Silverlight 4
https://tomasz.janczuk.org/2009/11/pubsub-sample-with-wcf-nettcp-protocol.html
Here, I’d like to demonstrate the steps for Silverlight 4 RC scenario, which means that the socket policy is exposed over the HTTP protocol at the root of the domain.
Ø Step 1:
Configure WCF service to use customBinding, and a metadata exchange endpoint based on NET.TCP schema as well. You can reference to below configuration:
<bindings>
<customBinding>
<binding name="NewBinding0">
<binaryMessageEncoding />
<tcpTransport />
</binding>
</customBinding>
</bindings>
…
…
<services>
<service behaviorConfiguration="NewBehavior" name="Demo.WCFService">
<endpoint address="WCFService" binding="customBinding" bindingConfiguration="NewBinding0"
name="ServiceEndpoint" contract=" Demo.IWCFService " />
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
name="MEXEndpoint" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://[domainB]:4503/" />
</baseAddresses>
</host>
</service>
</services>
Ø Step 2:
Deploy the clientaccesspolicy.xml at the root folder of https://domainB to make sure that the clientaccesspolicy.xml be accessible via https://domainB/clientaccesspolicy.xml. The clientaccesspolicy.xml should look like as follows:
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domain uri="*"/>
</allow-from>
<grant-to>
<socket-resource port="4502-4534" protocol="tcp" />
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
After above steps, your Silverlight should be able to call the self-hosted WCF service with NET.TCP binding.
Best Regards,
Winston.
Comments
- Anonymous
April 01, 2011
I found this to be very helpful. It's using localhost:80 for the client access policy but it can't use it if IIS is running locally. I have to shut down IIS. Do you have any suggestion on how to get around this? Thanks. - Anonymous
April 27, 2011
I understand you want it working without IIS running. If my understanding is correct, I think you can use WCF REST to publish :80/clientaccesspolicy.xml. Actually PORt 80 is still in listening status by HTTP.SYS in windows Server 2003. So it still works even you didn't install IIS6. wish it help you a little. Step1:Define an interface “IPolicyRetriever” :[ServiceContract] public interface IPolicyRetriever { [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")] Stream GetSilverlightPolicy(); [OperationContract, WebGet(UriTemplate = "/crossdomain.xml")] Stream GetFlashPolicy();} Step 2:Implement above interface in your WCF service:public class WCFService:IWCFService,IPolicyRetriever { //IWCFService implementation … …//IPolicyRetriever implementation private 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); } Step 3:Publish an endpoint which address is “ ” based on the host address of HTTP schema in order to accept the request for clientaccesspolicy.xml. We can achieve this by programmatically or administratively.Programmatically:Type serviceType = typeof(WCFService);ServiceHost host = new ServiceHost(serviceType);host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());Administratively:<behaviors> <endpointBehaviors> <behavior name="WebHttpNewBehavior"> <webHttp /> </behavior> </endpointBehaviors> ...</behaviors><services> <service behaviorConfiguration="NewBehavior"> … <endpoint behaviorConfiguration="WebHttpNewBehavior" binding="webHttpBinding" bindingConfiguration="" name="PolicyEndpoint" contract="WCFService.IPolicyRetriever" /> ... </service></services> - Anonymous
May 13, 2014
Thanks! that was a very clear example after gooooogling for a long long time