Silverlight to WCF Cross Domain SecurityException

March 31, 2010 12:38

In debugging a fairly simple Silverlight control that calls into a WCF service I ran into an often encountered security exception having to do with cross-domain policy. Silverlight generally does not allow cross domain access unless that access is explicitly granted via a clientaccesspolicy.xml file. Given that I had to read through several sources (see References below) to formulate a solution to this problem, I decided to pull those sources of information together here for future reference.

The Problem

I originally ran into the following dialog when trying to debug my main solution which consists of a couple of nested WCF services and a Silverlight application hosted in a web app (the control makes a call to one of the services which in turn makes a call to the other service).

Here is the exception:

CommunicationException

And the text:

Communication exception was unhandled by user code

An error occurred while trying to make a request to URI 'http://localhost:7249/Service1.svc'. 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. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute. Please see the inner exception for more details.

Examining the inner exception, I found:

{System.Security.SecurityException ---> System.Security.SecurityException: Security error.
   at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
   at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4
(Object sendState)
   at System.Net.Browser.AsyncHelper.<>c__DisplayClass2.<BeginOnUI>b__0(Object sendState)
   --- End of inner exception stack trace ---
   at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
   at System.Net.Browser.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.
HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)}

Given the somewhat complicated structure of the overall application, I decided to take a step back and create a very simple solution that contains a Silverlight app, its associated web hosting app, and a WCF service called “Service1”. I then added a service reference to “Service1” to the Silverlight app and ran the project. That got me back to the above CommunicationException. Good.

The Solution

There’s a lot of potential solutions to this problem (or so it seemed as I was working through it). What worked for me was adding a “clientaccesspolicy.xml” file to the root of the WCF service project. Here’s the contents of that file (and note that the name of the file is important as that is what Silverlight will look for by default):

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

This grants access across the board with no restrictions.

When I re-run the application it works as expected.

But, wait, there’s more…

One other issue I had was with the VS Dev Web Server using a virtual path like:

http://localhost:7249/MyService/Service1.svc

This can happen if you have folders inside your solution, for example. The problem is that Silverlight is looking for clientaccesspolicy.xml in the root folder of the service but it now exists in \MyService. Fortunately, there’s an easy way to correct this.

Thanks to Tim Heuer for the solution which is to examine Properties on the WCF service project:

image

The solution then is to remove the folder designation so the virtual path contains the root slash only:

image

 

I re-ran again and everything works.

References

[ Follow me on Twitter ]


A Silverlight TagCloud, Part 1: The WCF Service

July 23, 2009 09:44

The complete Silverlight TagCloud series of posts:

  1. A Silverlight TagCloud, Part 1: The WCF Service
  2. A Silverlight TagCloud, Part 2: The TagCloud
  3. A Silverlight TagCloud, Part 2.1: Refinements
  4. Silverlight TagCloud Now on CodePlex

This is the first of two posts about a new Silverlight TagCloud control I wrote. The architecture is simple: On the client-end we'll have a Silverlight control hosted in an ASP.NET page. On the server-end we'll have a WCF service that the TagCloud communicates with to get tag item information.

Here's the end result (minus some CSS formatting):

image

Since I knew I wanted to deploy the control/service combination to two different web sites, I opted to create both control and service projects in a new Visual Studio 2008 solution rather than adding them to each of the individual web projects where I'd have duplication of code. Turns out the process of getting the WCF bindings, service config, placement of the Silverlight XAP file, references, etc. all working correctly together when the service and control are not located inside the same solution as the web project was a major chore. Long story short, I figured it all out; I'll spare you my tales of woe and just give you the solution.

In this first post I intend to discuss the WCF service. The next post in the series will cover the Silverlight control.

Let me also add that since I use BlogEngine.NET as my hosting platform that the WCF service is tightly-bound with BlogEngine.NET's Post class. Replacing this underlying post/tag store shouldn't be that difficult, though, if you want to adapt this to your own blogging platform.

The TagCloudService

I started with a WCF Service Library since my thought was that a service library would make deployment to two different web sites easier (you can read about the differences between the WCF Service Application and Service Library templates here). Turns out it wasn't any easier or harder than if I'd just started with a WCF Service Application. In any case, I started with the usual Visual Studio 2008 WCF Service Library template:

image

After some renames and the addition of a TagCloudService.svc file (which you would have gotten for free using the WCF Service Application template), I wound up with this structure:

image

You'll notice that the TagCloudService.cs file is not tucked behind the TagCloudService.svc file in the usual code-behind manner. This is because the content of the .svc file is this:

  1: <%@ ServiceHost Language="C#" Debug="true" Service="TagCloud.TagCloudService.TagCloudService" %>

 

No CodeBehind attribute. This is so because I'll be deploying the compiled service DLL as the service's implementation, and not releasing the .cs file at all.

The Contract

Let's take a look at the service contract. Here's the entire file:

   1: using System.Collections.Generic;
   2: using System.Runtime.Serialization;
   3: using System.ServiceModel;
   4:  
   5: namespace TagCloud.TagCloudService
   6: {
   7:     [ServiceContract (Namespace = "TagCloud.TagCloudService")]
   8:     public interface ITagCloudService
   9:     {
  10:         [OperationContract]
  11:         List<CloudTag> GetTags (int threshold, string baseUrl);
  12:     }
  13:  
  14:     [DataContract]
  15:     public class CloudTag
  16:     {
  17:         public CloudTag (string tagName, string tagLink, int tagOccurrences)
  18:         {
  19:             TagName = tagName;
  20:             TagLink = tagLink;
  21:             TagOccurrences = tagOccurrences;
  22:         }
  23:  
  24:         [DataMember]
  25:         public string TagName;
  26:  
  27:         [DataMember]
  28:         public string TagLink;
  29:  
  30:         [DataMember]
  31:         public int TagOccurrences;
  32:     }
  33: }

The service contract defines just one operation: GetTags. It takes as parameters the tag occurrence threshold, which is a minimum number of times a tag must have been used in our blog before we'll display it in the TagCloud control, and the baseUrl, which is the hosting web site's base address, as in http://www.itscodingtime.com. We'll see how the base url is used in a moment. GetTags returns a collection of CloudTag objects.

CloudTag is defined with the [DataContract] attribute; it's members are [DataMember]'s. CloudTag holds relevant information for a single tag on our blog, including the name of the tag, the url to the tag so that visitors can click on a tag in the cloud and be taken to the corresponding posts, and the number of occurrences of the tag.

The Implementation

The service implementation is rather short:

   1: using System.Collections.Generic;
   2: using System.ServiceModel.Activation;
   3: using BlogEngine.Core;
   4:  
   5: namespace TagCloud.TagCloudService
   6: {
   7:     [AspNetCompatibilityRequirements (RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   8:     public class TagCloudService : ITagCloudService
   9:     {
  10:         public List<CloudTag> GetTags (int threshold, string baseUrl)
  11:         {
  12:             var cloudTagCollection = new List<CloudTag> ();
  13:  
  14:             if (Post.Posts == null)
  15:             {
  16:                 return (cloudTagCollection);
  17:             }
  18:  
  19:             // get all tags and the number of times each occurs
  20:             var sortedDict = new SortedDictionary<string, int> ();
  21:             foreach (var post in Post.Posts)
  22:             {
  23:                 if (!post.IsVisible) continue;
  24:  
  25:                 foreach (var tag in post.Tags)
  26:                 {
  27:                     if (sortedDict.ContainsKey (tag))
  28:                         sortedDict[tag]++;
  29:                     else
  30:                         sortedDict[tag] = 1;
  31:                 }
  32:             }
  33:  
  34:             // we have all the tags. only save those that meet our minimum occurrences threshold
  35:             foreach (var tag in sortedDict)
  36:             {
  37:                 if (tag.Value > threshold)
  38:                 {
  39:                     cloudTagCollection.Add (new CloudTag (tag.Key, baseUrl + "/?tag=/" + tag.Key, tag.Value));
  40:                 }
  41:             }
  42:  
  43:             return (cloudTagCollection);
  44:         }
  45:     }
  46: }

 

The main part is, of course, the GetTags operation, which utilizes BlogEngine.NET's Posts collection to retrieve all tags and the number of times each appears across the blog. It then adds only those tags that meet the minimum threshold to the CloudTag collection. This collection is then what is returned to the consuming app, which in this case will be the Silverlight control.

Deployment

You can deploy the WCF service right now (since nothing is actually using it yet). I've written a couple of posts on how to do that, but what you basically need to do is make the needed changes to your web.config, then copy over the .svc and assembly DLL. You can then access the service to make sure it's running with, for example, http://www.itscodingtime.com/TagCloudService.svc.

Conclusion

As you can see the WCF service part of the implementation is pretty straightforward. Deployment of the service is (hopefully) no more difficult.

The second part of this post will discuss the consumer of this service: the Silverlight TagCloud control. Until then.

Download: Silverlight TagCloud control on CodePlex


The different ways to host a WCF Service Application in a Web App

July 20, 2009 08:51

There are at least three different ways to configure the hosting of a WCF Service Application inside a Web Application.

They are:

  1. Add a WCF Service directly via "Add New Item…"
  2. By copying files, including the service implementation code-behind file
  3. By copying files, including the compiled, service implementation DLL

In this post, I'm going to take a walk through each of these methods.

Method #1: Add a WCF Service directly via "Add New Item…"

This is the most straightforward of the methods. Right-click on the Web App project name and select Add | New Item…:

image

Then, select WCF Service and click Add:

image

All the grunt work has been done for you: a reference to System.ServiceModel has been added, the Web App's web.config has been modified, and the service contract and implementation files have been added. Your project layout will look something like this (new files highlighted):

image

That's it for this method. You can run and see the service by navigating to Service1.svc. But this model does not fit all situations. To see why, let's move on to Method #2.

Method #2: By copying files, including the service implementation code-behind file

The first method is fine when you have a Web Application and just want to host a WCF service in that one and only project. But what about reusability? If you want to use the WCF service across two or more web sites you generally don't want to have duplicate code existing in two different places. You can create a base class, and derive each service from that, but I'd rather just have the entire service contained within a single project. Then it's just a matter of deploying the service onto the different web sites (or web projects).

One way of doing that is to copy the service's contract, implementation, and config into the web project:

1.) First, create a new WCF Service Application in a separate solution.

2.) Copy each of the following files from the root of the service application folder to the root of the web application folder:

  • IService1.cs
  • Service1.svc
  • Service1.svc.cs

Right-click on your web app project name and add these files via the Add Existing Item… dialog.

3.) Add a reference to System.ServiceModel:

image

4.) From your WCF Service Application's web.config, copy the <system.serviceModel> section and paste into your Web Application's web.config (right after the </runtime> closing tag).

5.) Run the Web Application and navigate to Service1.svc. You should see the service.

That's not horrible; you can probably automate most of the file copies and really only need to add the files themselves to the web project the first time. There is, however, a slightly easier way.

Method #3: By copying files, including the compiled, service implementation DLL

The WCF Service Application compiles down to a DLL. Let's take advantage of that. While this step doesn't differ that much from Method #2, it is another alternative.

1.) Compile the WCF Service Application.

2.) Copy the resulting DLL into the Web App's bin folder or alternatively an "imports" folder.

3.) In the Web Application, add a reference to the service DLL:

image

4.) Add a reference to System.ServiceModel.

5.) Copy only the Service1.svc file (not the code-behind or contract files) from the service project folder to the web project folder and add it to the Web Application via the Add Existing Item… dialog.

6.) Open the Service1.svc file and remove the CodeBehind attribute from ServiceHost.

4.) From your WCF Service Application's web.config, copy the <system.serviceModel> section and paste into your Web Application's web.config (right after the </runtime> closing tag).

5.) Run the Web Application and navigate to Service1.svc. You should see the service.

It's really the same number of steps as Method #2. The biggest difference is that instead of copying over the contract and implementation files, you instead copy the single DLL. The advantage of this method, however, is that once you've done the initial setup (add references, copy over config info) the only thing you'll have to do if the service implementation changes is copy over the DLL.

Conclusion

There's never only one way of doing these things, but this is how I approach web hosting of a WCF service. I prefer Method #3 because I then really only need to worry about deploying a single DLL once the initial hosting setup is done. I like Method #1 the least, especially for a service which I intend to use across multiple web sites. Having the entire WCF service contained within a single location/solution is simply a best practice and helps promote code reusability and ease of maintenance.

References

Deploying an Internet Information Services-Hosted WCF Service


How to workaround Could not load file or assembly App_Web_ The system cannot find the file specified

July 17, 2009 07:49

Even though many others have blogged solutions to this issue (presenting a wide variety of potential workarounds, I might add), I'm posting this anyway for my own reference. I'd especially like to log the details specific to my situation as I'm seeing it a bit too much lately on my production (hosted) site.

Some suggest deleting temporary internet files, another suggests making a small change to the web.config file, Rick Strahl suggests a full recompile, and Scott Guthrie talks about the official Microsoft hotfix and suggested workarounds.

Before I get into the solution, I'll take a moment to look at my situation and the symptoms I was seeing.

The Symptoms

I'm running a Silverlight control, hosted in an ASP.NET web app which also hosts a WCF service. When I load up the main web aspx page my Silverlight control doesn't do anything. IE displays the yellow error icon in the lower left; clicking on it brings up this:

image

Copying error details yields:

Webpage error details

User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 3.5.30729; Tablet PC 2.0; OfficeLivePatch.1.3; .NET CLR 3.0.30729; OfficeLiveConnector.1.4)
Timestamp: Thu, 16 Jul 2009 00:32:45 UTC

Message: Unhandled Error in Silverlight 2 Application An exception occurred during the operation, making the result invalid.  Check InnerException for exception details.   at System.ComponentModel.AsyncCompletedEventArgs.RaiseExceptionIfNecessary()
   at TagCloud.TagCloudControl.TagCloudService.GetTagsCompletedEventArgs.get_Result()
   at TagCloud.TagCloudControl.Page.tagCloudService_GetTagsCompleted(Object sender, GetTagsCompletedEventArgs e)
   at TagCloud.TagCloudControl.TagCloudService.TagCloudServiceClient.OnGetTagsCompleted(Object state)

Because this Silverlight control calls into a WCF service (and because I'd seen this exception before), I immediately went to my service's .svc file. Trying to bring that up in the browser brought up this:

Could not load file or assembly 'App_Web_ivxpj_7c, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

sshot-69

The Solution(s)

There's more than one solution to this problem. I'll detail the ones I found, but keep in mind people have had various levels of success with most of them.

  1. If you're seeing this problem on your development machine, do a full recompile. This one seemed to do the trick… sometimes.
  2. If you're seeing this problem on your development machine, delete temporary internet files. I had no success with this one.
  3. If you're seeing this problem on your development machine, remove and re-add the WCF service dll reference (assuming you have a dll reference). This is a home-grown solution which worked every time for me but was kind of a pain.
  4. If you're seeing this problem on your (hosted) server, copy over the service code or dll, overwriting the existing file(s). This should force a recompile by IIS. I have had good success with this one.
  5. Modify your web.config by adding the batch attribute. I haven't tried this one yet, though others have reported success with it.
   1: <system.web>
   2:     <compilation defaultLanguage="c#" debug="false" batch="false"/>
   3: </system.web>

I am looking forward to trying solution #4, but I'm hesitant to deploy any changes to my web.config. I just got everything working again. ;-)