The Darkside

Shedding light on things and stuff

 
  Home :: Contact :: Syndication  :: Login
  75 Posts :: 0 Stories :: 49 Comments :: 2 Trackbacks

Ads

Archives

Post Categories

Open Source Projects

Other Blogs

If you've decided to self-host a WCF service - or as in my case you already have a service in production but need it to be consumed by a Silverlight application, but keep on getting a security exception, here's a method of getting your existing services to work. It involves getting the WCF service to serve a "file" called "clientaccesspolicy.xml" when one is requested, as is done by Silverlight. 

DOWNLOAD - WCF4Silverlight.Zip

Firstly, if you haven't already, you'll need to expose your services using an http binding. In my example project, I have a service MyService which is exposed using netTcp. My configuration file originally looked like this:

 Expand Code
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="enableMetaData" >
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="Darkside.WCF4Silverlight.MyService" behaviorConfiguration="enableMetaData">
        <host >
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:9000"/>
          </baseAddresses>
        </host>
        <endpoint address="Calculator" binding="netTcpBinding" contract="Darkside.WCF4Silverlight.IMyService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

I modified the configuration file to look like this: 

 Expand Code
    1 <?xml version="1.0" encoding="utf-8" ?>
    2 <configuration>
    3   <system.serviceModel>
    4     <behaviors>
    5       <serviceBehaviors>
    6         <behavior name="enableMetaData" >
    7           <serviceMetadata httpGetEnabled="true" />
    8         </behavior>
    9       </serviceBehaviors>
   10       <endpointBehaviors>
   11         <behavior name="webHttpBehavior">
   12           <webHttp/>
   13         </behavior>
   14       </endpointBehaviors>
   15     </behaviors>
   16     <services>
   17       <service name="Darkside.WCF4Silverlight.MyService" behaviorConfiguration="enableMetaData">
   18         <host >
   19           <baseAddresses>
   20             <add baseAddress="net.tcp://localhost:9000"/>
   21             <add baseAddress="http://localhost:9001"/>
   22           </baseAddresses>
   23         </host>
   24         <endpoint address="Calculator" binding="netTcpBinding" contract="Darkside.WCF4Silverlight.IMyService" />
   25         <endpoint address="Calculator" binding="basicHttpBinding" contract="Darkside.WCF4Silverlight.IMyService" />
   26         <endpoint address="" binding="webHttpBinding" behaviorConfiguration="webHttpBehavior" contract="Darkside.WCF4Silverlight.IClientAccessPolicy" />
   27       </service>
   28     </services>
   29   </system.serviceModel>
   30 </configuration>

You'll notice on line 11 I added a new behaviour, on line 21 I added a new base address for the http exposed services, and I've added two extra endpoints on lines 25 and 26.

The first additional endpoint (line 25) is to expose my existing service over an http binding that can be consumed by Silverlight. The second additional endpoint (line 26), which has no address, is what we'll use to serve the file. It also has the new behaviour added to it to enable it as a REST service.

On a side note: If you don't have this behaviour configuration configured, you may an error along the lines of "The message with To '*' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree". Originally, before I had the behaviour in and I requested the URL in my browser all worked well. Suddenly, after making some minor changes, the error above occurred and didn't stop until the behaviour was configured.

Back in your source code, you need to add a contract as follows:

[ServiceContract]
public interface IClientAccessPolicy
{
    [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
    Stream GetClientAccessPolicy();
}

In your service, modify the definition of the class to also implement this new contract. The implementation of the GetClientAccessPolicy method is included in the excerpt below:

 Expand Code
   public class MyService : IMyService, IClientAccessPolicy
    {
        //Existing code...
 
        public Stream GetClientAccessPolicy()
        {
            const 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>";
 
            if (WebOperationContext.Current != null)
                WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
            return new MemoryStream(Encoding.UTF8.GetBytes(result));
        }
    }

You can now start the service host, open up your browser and, if you're using the sample application above, enter the url http://localhost:9001/clientaccesspolicy.xml. You should get a response along the lines of this displayed in your browser:

<?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>

Now you can safely reference your service from you Silverlight application, and the cross domain security issues should be resolved, as the request for the file "clientaccesspolicy.xml" by the application will now be responded to by your service.
posted on Thursday, April 09, 2009 4:44 AM
Comments have been closed on this topic.