This blog post will show you how to allow a Silverlight application to call a service over the Windows Azure AppFabric Service Bus. The problem you need to solve is that Silverlight will look for a “clientaccesspolicy.xml” at the root uri of the service. When I tried it myself I couldn’t find any “how to” on this topic so I decided to turn this into a blog post. If anyone else has this blogged, sorry I am such a bad internet searcher .
So, you’ve just build a nice Silverlight application that uses some WCF service you’re hosting locally. You’ve done all the steps to make it work on your machine, including the “clientaccesspolicy.xml” to enable cross-domain communication. The only thing is that you want to keep hosting the service locally and/or move it to another machine without updating the Silverlight client.
You’ve heard that the Windows Azure Service Bus allows you to do this more easily so you decide to use it. This is your current service configuration (notice the localhost address!).
Code Snippet
- <service name="SilverlightAndAppFabric.TheService" >
- <endpoint name="HELLO"
- address="http://localhost:1234/rest"
- behaviorConfiguration="REST"
- binding="webHttpBinding"
- bindingConfiguration="default"
- contract="SilverlightAndAppFabric.IHello" />
- </service>
What you now need to do is to move it to the AppFabric Service bus. This is easy. Of course you need to get a subscription for Windows Azure and set up the AppFabric service bus… Look for somewhere else on this, there’s lots of this around.
Then you change the address, binding and behavior like this:
You need an endpoint behavior, because your service needs to authenticate to the service bus (so they can send you the bill):
Code Snippet
- <endpointBehaviors>
- <behavior name="REST">
- <webHttp />
- <transportClientEndpointBehavior>
- <clientCredentials>
- <sharedSecret
- issuerName="owner"
- issuerSecret="---your secret key here please---" />
- </clientCredentials>
- </transportClientEndpointBehavior>
- </behavior>
- </endpointBehaviors>
You (might) need a binding configuration to allow clients to access your service anonymously:
Code Snippet
- <webHttpRelayBinding>
- <binding name="default" >
- <security relayClientAuthenticationType="None">
- </security>
- </binding>
- </webHttpRelayBinding>
And of course you need to change the endpoint to use the WebHttpRelayBinding:
Code Snippet
- <endpoint name="HELLO"
- address="https://u2utraining.servicebus.windows.net/rest"
- behaviorConfiguration="REST"
- binding="webHttpRelayBinding"
- bindingConfiguration="default"
- contract="SilverlightAndAppFabric.IHello" />
This should to the trick. Yes, when you try the REST service using Internet Explorer you get back the intended result.
Now you update the address in your Silverlight application to use the service bus endpoint:
This is the old call:
Code Snippet
- wc.DownloadStringAsync(new Uri("http://localhost:1234/rest/hello"));
And you change it to:
Code Snippet
- wc.DownloadStringAsync(new Uri("https://u2utraining.servicebus.windows.net/rest/hello"));
Please note the switch to https and the service bus address.
You run your Silverlight client and it fails with some strange security error! The problem is that Silverlight will try to access the clientaccesspolicy.xml file from your new address. Since this is now the service bus this will not work. To solve it you simply add another REST endpoint that will return the clientaccesspolicy from this Uri. Start with the service contract:
Code Snippet
- [ServiceContract]
- public interface IClientAccessPolicy
- {
- [OperationContract]
- [WebGet(UriTemplate = "clientaccesspolicy.xml")]
- Message GetPolicyFile();
- }
Implement it:
Code Snippet
- public Message GetPolicyFile()
- {
- WebOperationContext.Current.OutgoingRequest.ContentType = "text/xml";
-
- using (FileStream stream = File.Open("clientaccesspolicy.xml", FileMode.Open))
- {
- using (XmlReader xmlReader = XmlReader.Create(stream))
- {
- Message m = Message.CreateMessage(MessageVersion.None, "", xmlReader);
- using (MessageBuffer buffer = m.CreateBufferedCopy(1000))
- {
- return buffer.CreateMessage();
- }
- }
- }
- }
And make sure it returns the right policy. This is what gave me a lot of headache, so here it is:
Code Snippet
- <?xml version="1.0" encoding="utf-8"?>
- <access-policy>
- <cross-domain-access>
- <policy>
- <allow-from http-request-headers="*">
- <domain uri="http://*"/>
- <domain uri="https://*"/>
- </allow-from>
- <grant-to>
- <resource path="/" include-subpaths="true"/>
- </grant-to>
- </policy>
- </cross-domain-access>
- </access-policy>
Pay special attention to the allow-from element. By default this will allow SOAP calls, not REST calls.
For explanations read the documentation. You might want to edit it anyway.
Now add a similar REST endpoint, making sure the clientaccesspolicy is at the root level:
Code Snippet
- <endpoint name="CLIENTACCESSPOLICY"
- address="https://u2utraining.servicebus.windows.net"
- behaviorConfiguration="REST"
- binding="webHttpRelayBinding"
- bindingConfiguration="default"
- contract="SilverlightAndAppFabric.IClientAccessPolicy" />
Done! A working example (you will have to change the client credentials to your own) can be downloaded from the U2U site here.