This is the final post of a trifecta examining Microsoft “Dallas”, a marketplace for data services announced at PDC 2009. In my first post, I provided an overview of “Dallas”, including how to access various trial data services via the developer portal and in code.
I followed that up last week with a post that walks through modifying the auto-generated C# service proxy classes to provide asynchronous invocation capabilities, a must have for Silverlight client access and, in general, a good way to go to keep your application UI responsive in the face of slow or unpredictable network performance.
For this article, I’m going to leverage the code changes I made in the last article (to enable asynchronous access) to build a Silverlight application that accesses the same Data.gov crime statistics that the previous Windows Forms application did. As I mentioned in that post, Silverlight has two primary constraints in terms of accessing resources and services over the web:
- Access must be asynchronous, and
- Resources must be accessed from the point-of-origin of the Silverlight application (although there are options we’ll discuss next for working around this).
Handling Cross-Domain Access
Asynchronous access was addressed in the last post, so now we need to deal with enabling Silverlight applications downloaded from my web host to access services hosted on “Dallas”, namely the api.sqlazureservices.com domain. With Silverlight there are two ways to handle this:
- Use a proxy; that is, set up a service hosted on the same domain as my Silverlight application that acts as a go-between to the “Dallas” service. The Silverlight application is talking to a service that’s at its point-of-origin, and that service is a simple client to the ultimate service hosted by “Dallas.” Although it forces an extra hop in the communication chain, it’s a fairly common pattern for handling the ‘point-of-origin’ constraint.
- Set up a policy file on the service host, here api.sqlazureservices.com. Before attempting to access a non point-of-origin service, a Silverlight application will check to see if the targeted server has a clientaccesspolicy.xml file at the root of its domain. This file is an XML document containing information on what clients are allowed access to the server and what resources on the server they can access. If a clientaccesspolicy.xml file is not found, Silverlight will then look for a crossdomain.xml file, which is an XML file used for similar purposes by Adobe Flash applications.
“Dallas” has just recently setup up a clientaccesspolicy.xml file (below) to permit Silverlight application access, so that’s the route I’m going to pursue here. The complete format of the file is described in the MSDN documentation, but the salient features required for “Dallas” include:
- Line 5 – specification of the request headers that are sent by the client. If the client includes headers not specified here, the request is rejected. The headers sent are, of course, the $accountKey and the $uniqueUserID.
- Lines 6-7 – specification of the client URLs allowed access. Since “Dallas” is accessible only via HTTPS, the domain tags are necessary to enable a Silverlight application downloaded via HTTP to access the “Dallas” server.
- Line 10 – specification of which resources (URIs) the client can access on the “Dallas” server; here, it’s everything below the domain root.
1: <?xml version=”1.0″ encoding=”utf-8″ ?>
5: <allow-from http-request-headers=”$accountKey,$uniqueUserID”>
6: <domain uri=”http://*” />
7: <domain uri=”https://*” />
10: <resource path=”/” include-subpaths=”true” />
A Few More Tweaks
At this point, you’d figure it would just work; we’ve got code to make an asynchronous call, and the cross-domain access issue is resolved via the policy file, right? Well, not quite.
Silverlight runs on the CLR, but it’s a subset of the full-blown .NET CLR (one that fits within a 5MB or so download). When I built the asynchronous invocation code in the previous post, I did so for a Windows Forms application, and when pulling that same code into my Silverlight application, I ended up with a few compilation issues.
- In the code that converts the XML (Atom format) response stream into the data transfer object class (DataGovCrimeByCitiesItem), the static method Convert.ChangeType is frequently invoked. In Silverlight, that method has fewer overloads than in the full .NET Framework, so I had to add a final parameter value (null) to each of the calls. That parameter is a IFormatProvider, which in this case is inconsequential since we are not presenting the data in this step.
Now, that same issue exists in the auto-generated code for the synchronous invocation as well (along with some additional issues). In Silverlight though, the synchronous invocation is not an option, so you’ll need to comment out or otherwise remove the Invoke and InvokeWebService methods that are part of the C# code generated when you select the Download C# service classes… option on the “Dallas” Developer Portal.
- The code that assigned the $accountKey and $uniqueUserID to the HTTP Request header looks like this in my first attempt:
Unfortunately, Silverlight does not support the Add method on a WebHeaderCollection, but the remedy is rather simple:
request.Headers[“$accountKey”] = this.AccountKey;request.Headers[“$uniqueUserID”] = this.UniqueUserID.ToString();
With that, the code compiles just fine, but at runtime you’ll get a System.NotSupportedException with the less than helpful message that “Specified method is not supported” and pointing to the line:
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(ar);
Using the Silverlight Client stack
Well, certainly EndGetResponse is supported in Silverlight – after all MSDN says so, and it would be tough to make asynchronous calls (the only type allowed) if it weren’t. What’s happening here is that restrictions in the browser HTTP stack, which Silverlight leverages by default, prevents the call from succeeding. The reason is somewhat buried in the MSDN documentation:
You can send headers, if they are allowed per the client access policy file, by setting the HttpWebRequest.Headers property. If you are sending headers on anything other than POST requests, you must use Client HTTP handling.
While we took care of the client access policy file, the existing code to create the request
HttpWebRequest request =
(HttpWebRequest) HttpWebRequest.Create(new Uri(builder.ToString()));
request.Headers[“$accountKey”] = this.AccountKey;
request.Headers[“$uniqueUserID”] = this.UniqueUserID.ToString();
defaults to browser HTTP handling, and since the default HTTP verb here is a GET and we’re setting headers to pass in the $accountKey and $uniqueUserID, we’re doomed to failure.
You might be inclined to change the request type to a POST via the request.Method property, but that will result in an HTTP 405 (Method not allowed) error since the “Dallas” service is not configured to accept POST requests. That leads to the only other option here, using Client HTTP handling.
Client HTTP handling was introduced with Silverlight 3 and provides additional HTTP support over the default browser handling, including REST service calls, use of HTTP verbs other than GET and POST, and support for additional status codes. To opt in to client HTTP handling, you create the WebRequest using the ClientHttp property of the WebRequestCreator static class, so the HttpWebRequest instantiation code directly above would be modified to
HttpWebRequest request = (HttpWebRequest)
With that last change, the Silverlight example executes successfully:
With the modification of the code above to use the Silverlight Client HTTP stack, I’ve now complicated my code base. The non-Silverlight scenarios (WPF, Windows Forms, etc.) can’t use this code – they won’t even compile – because the System.Net.Browser namespace is Silverlight only. As a result I need two different calls to instantiate the request depending on the runtime context of the application.
One option to resolve this is compiler directives, where the HttpWebRequest creation depends on the compilation environment. This works fine for Silverlight 3 since Silverlight and non-Silverlight assemblies are not binary compatible – meaning you need to recompile the code anyway.
HttpWebRequest request = (HttpWebRequest)
HttpWebRequest request = (HttpWebRequest)
In Silverlight 4 though, Microsoft announced binary compatibility of Silverlight with non-Silverlight assemblies, so the compiler directive isn’t as attractive a feature.
Other alternatives exist though, such as using the WebRequest.RegisterPrefix method to map some or all Silverlight requests to the Client HTTP stack. This initialization need be made only once (say in the constructor of the service class) and then the WebRequest.Create code will automatically use the Client versus the Browser HTTP stack. Then, there’s no longer a need for the compiler directive in the code above (although a similar scenario arises when calling RegisterPrefix).
You could probably obviate the use of the compiler directive using reflection too. Additionally, Dependency Injection (say using Unity) is perhaps a more elegant option, but certainly adds more complexity to the application. Given that “Dallas” is still a work in progress, and it’s likely that the release version will have a more robust implementation for the service classes, I’ll resist the temptation to over-engineer and leave it to the real developers in Redmond to work it all out for the public release!
I’ve posted the code discussed above, along with the updated version of the Windows Forms example that uses the same service class implementation. Of course, to run it you’ll still need to get your own Dallas token.