Hosting Silverlight Application in a WCF Rest Service

Typically Silverlight apps are hosted in IIS or on some other web server. Getting a website setup can be a tedious process and constantly deploying can be annoying especially if the service is not an integral one to the functionality of an application. A WCF Rest service is an easy way to test Silverlight applications or host utility apps that don’t need to be especially scalable.

I find it convenient also to reference web services used by the Silverlight client relative the base url of the rest service (the url of the browser).

[ServiceContract]
public interface IFrameworkRestService
{
    [OperationContract]
    [WebGet(UriTemplate="clientaccesspolicy.xml")] 
    Stream ClientAccessPolicy();

    [OperationContract]
    [WebGet(UriTemplate = "{fileName}")]
    Stream GetFile(string fileName);

    [OperationContract]
    [WebGet(UriTemplate = "")]
    Stream RootHtmlPage();
}

As with all WCF services, attributes are used to indicate a particular class is a web service. Additionally each method is marked with the OperationContract attribute. The WebGet attribute indicates a particular method should be accessed in a restful way. The UriTemplate parameter defines how the operation can be accessed relative the base URL used to expose the service. For example if the service is exposed on the URL, “http://localhost/MyService”, the ClientAccessPolicy can be executed by calling the URL, “http://localhost/MyService/clientaccesspolicy.xml”. The RootHmtlPage can be accessed through the URL, “http://localhost/MyService” as an empty string is passed to the UrlTemplate attribute. The GetFile method has a special parameter, with brackets around the name, that maps to any URL after the ‘MyService’ part of the URL. This method will serve up the Xap Silverlight files. Some examples would be “http://localhost/MyService/Test1.xap” and ‘http://localhost/MyService/Test2.xap’. The name of the file is passed into the GetFile method in the fileName parameter.

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, AddressFilterMode=AddressFilterMode.Any)]
public class FrameworkRestService : IFrameworkRestService
{
    private readonly ILogger logger;

    public FrameworkRestService(ILogger logger)
    {
        this.logger = logger;
    }

    public Stream ClientAccessPolicy()
    {
        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));
    }

    public Stream GetFile(string fileName)
    {
        string path = AppDomain.CurrentDomain.BaseDirectory;
        string absolutePath = string.Format(@"{0}\{1}", path, fileName);

        logger.Log(string.Format("Serving up {0}.", absolutePath));

        try
        {
            return new FileStream(absolutePath, FileMode.Open);
        }
        catch(Exception e)
        {
            logger.Log("Exception getting file.", e);
        }

        return null;
    }

    public Stream RootHtmlPage()
    {
        if (WebOperationContext.Current != null)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
        }

        string path = AppDomain.CurrentDomain.BaseDirectory;

        logger.Log("Serving up root MAClient html page.");

        try
        {
            return new FileStream(string.Concat(path, @"ClientTestPage.html"), FileMode.Open);
        }
        catch(Exception e)
        {
            logger.Log("Exception retrieving root html page.", e);
        }

        return null;
    }
}

Couple things to note in the implementation of the REST service. Don’t forget to set the MIME type when serving up the root html page and the clientaccesspolicy. Not sure why but the browser has trouble figuring out what it’s getting if this isn’t set. It is assumed that the Silverlight HTML files and Xap files are located in the directory that the application is running from but this could be set to anywhere in the GetFile method.

The test html page created by Visual Studio usually references the base Silverlight xap file within the ClientBin directory. Edit the html file and remove the ClientBin from before the xap file reference. Otherwise the GetFile method won’t be called because it won’t match the UriTemplate of the GetFile interface method.

public class RestServiceHost
{
    private readonly IFrameworkRestService;
    private readonly WebServiceHost restServiceHost;

    public RestServiceHost()
    {
        IFrameworkRestService restService = new FrameworkRestService(new Logger());
        restServiceHost = new WebServiceHost(restService, new Uri("http://localhost/MyService", UriKind.Absolute));

        restServiceHost.Open();
    }

}

Use the WebServiceHost rather than ServiceHost as it is better suited for hosting Rest services (less configuration required). Once these services are running navigate your browser to http://localhost/MyService and the Silverlight app should appear.

Advertisements

2 thoughts on “Hosting Silverlight Application in a WCF Rest Service

  1. This is great, but with large file sizes, it fails to download file any idea? I tried 400 mb file.

  2. Pingback: silver wardrobe

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s