metadevblog

Windows Phone 7 App Development

Using Silverlight with a Windows Service – Part 2

with one comment

In order to get the Silverlight version of the application to work the Windows Service has to be modified so that it can provide the necessary Silverlight clientaccesspolicy.xml file or Flash policy file crossdomain.xml from root domain of the service.

This is accomplished by adding a WebService to the WindowService running on the same port as the existing Data Service and using a URL rewriter to provide the correct xml file to the Silverlight application.

First add empty IWebService.cs and WebService.cs classes.

The IWebService.cs should be replaced with this code:

using System.ServiceModel;
using System.IO;
using System.ServiceModel.Web;

namespace ConsoleApplicationWCF
{
    [ServiceContract]
    public interface IWebServer
    {
        [OperationContract, WebGet(UriTemplate = "ClientAccessPolicy.xml")]
        Stream GetClientAccessPolicy();
    }
}

 

Note: You will need to add a reference to System.ServiceModel.Web which can only be added when the dialog is filtered by .NET 4.0 Framework (and not the Client framework).

The key difference between this interface and the one use for the DataService is the WebGet() that has been added to the Operation Contract and it will invoke the GetClientAccessPolicy() method when a GET request matches the template.  The more typical use of this pattern is to respond to more generic request pattern, however it is ideal for handling the Silverlight request.

http://localhost:1234/clientaccesspolicy.xml

The code to handle the request is also very simple:

using System.IO;
using System.ServiceModel.Web;
using System.Reflection;

namespace ConsoleApplicationWCF
{
    public class WebServer : IWebServer
    {
        public const string AssemblyRootPath = "ConsoleApplicationWCF.";

        public Stream GetClientAccessPolicy()
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
            return Assembly.GetExecutingAssembly().
               GetManifestResourceStream(AssemblyRootPath + "ClientAccessPolicy.xml");
        }
    }
}

 

The AssemblyRootPath is the namespace for the application.  This should be ConsoleApplicationWCF but can be confirmed by looking at the Default Namespace in the Projects Properties.

Add the following XML as a ClientAccessPolicy.xml file to the project and make sure that the Build Action for the file is set to ‘Embedded Resource’.  When the project is built the xml file will be built into the binary file.

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

 

When the method is invoked the file data is returned to the caller.

The final part of the process is to create a web server endpoint to handle this contracted request.

   <behaviors>
    <endpointBehaviors>
      <behavior name="WebServerEndpointBehavior">
        <webHttp />
      </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
      <behavior name="WebServerBehavior">
        <serviceMetadata httpGetEnabled="false" />
        <serviceDebug httpHelpPageEnabled="false" includeExceptionDetailInFaults="true" />
      </behavior>
        ...
    </serviceBehaviors>
   </behaviors>

 

Notice that there is an endpointBehaviour and a serviceBehaviour. 

These two behaviours are used in the definition of the WebServer service:

    <service behaviorConfiguration="WebServerBehavior" name="WebServer">
      <endpoint address="" behaviorConfiguration="WebServerEndpointBehavior"
        binding="webHttpBinding" contract="IWebServer">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:1234/" />
        </baseAddresses>
      </host>
    </service>

 

Notice that the <service> behaviourConfiguration uses the WebServerBehaviour and the <endpoint> behaviourConfiguration uses the WebServerEndpointBehaviour.  I know that this looks simple but it’s driven me mad while configuring WCF previously – the trick I think is to make sure the names are really reflecting the configuration!

The binding for the WebServer is ‘webHttpBinding’ which will respond to the HTTP GET method that Silverlight makes (and notice that the GetClientAccessPolicy() outgoing content type is ‘text/html’

Here is the full service configuration for both the WebService and the DataService:

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WebServerEndpointBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="WebServerBehavior">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug httpHelpPageEnabled="false" includeExceptionDetailInFaults="true"/>
        </behavior>
        <behavior name="DataServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="WebServerBehavior" name="ConsoleApplicationWCF.WebServer">
        <endpoint address="" behaviorConfiguration="WebServerEndpointBehavior" 
             binding="webHttpBinding" contract="ConsoleApplicationWCF.IWebServer">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1234/"/>
          </baseAddresses>
        </host>
      </service>
      <service behaviorConfiguration="DataServiceBehavior" name="ConsoleApplicationWCF.DataService">
        <endpoint address="" binding="basicHttpBinding" contract="ConsoleApplicationWCF.IDataService">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1234/DataService"/>
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

And now when the Silverlight application runs and the button is pressed it will request the client access policy file, validate it and then assuming its all OK will fetch the date and time. Awesome!

image

Advertisement

Written by metadevblog

March 28, 2011 at 10:58 pm

Posted in Silverlight, WCF

Tagged with ,

One Response

Subscribe to comments with RSS.

  1. Hi Thanks a lot for post I have exactly same but still facing the same cross domain issue.
    Below is the error:
    An error occurred while trying to make a request to URI “http://localhost:8906/” . This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent

    Struggling for many days please help Thanks a lot in advance

    neeraj

    June 29, 2012 at 8:19 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: