Silverlight: How to Make Your WCF Web Service Work for All Users


Specification of the Problem


A WCF web service works when a user types site in his browser with www in the name:


·          http://www.nokola.com/SampleService/ServiceClientAppTestPage.aspx Works OK!,


 


but it stops working if the same site is typed without www:


·         http://nokola.com/SampleService/ServiceClientAppTestPage.aspx  Fails with error message:


“The remote server returned an unexpected response: 404 Not Found”


 


Note: the samples in this article are using Silverlight 2 Beta 2.


Why is This Happening?


WCF Web services work currently with one base Uri only. Some website hosts (if not most) pass 2, 3 or more base Uri-s to a web service when they activate it.


For example for the above web service the based addresses passed during activation may look like this:


http://www.nokola.com/SampleService/ServiceClientAppTestPage.aspx  


http://nokola.com/SampleService/ServiceClientAppTestPage.aspx  


http://something.something.nokola.com/SampleService/ServiceClientAppTestPage.aspx


 


If we don’t do anything special to filter the base addresses, our web service creation will fail. If I remember correctly, the error message looks like this:


 


This collection already contains an address with scheme http.  There can be at most one address per scheme in this collection.


 


Parameter name: item


 


To work around this issue, I create a custom ServiceHostFactory and return only one base address:


public class MyServiceHostFactory : ServiceHostFactory {


    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)   {


        return new CustomServiceHost(serviceType, new Uri(“http://www.nokola.com/SampleService/ServiceClientAppTestPage.aspx “));


    }


}


 


public class CustomServiceHost : ServiceHost {


    public CustomServiceHost(Type serviceType, params Uri[] baseAddresses)


        : base(serviceType, baseAddresses) { }


 


    protected override void ApplyConfiguration() {


        base.ApplyConfiguration();


    }


}


 


Read more about the custom factory here:  http://blogs.msdn.com/nikola/archive/2008/03/12/visual-studio-2008-walkthrough-creating-hosting-and-using-wcf-services-with-silverlight-2-beta-1.aspx


 


Because we can return only one address, our service is usable only from this address. That’s why when I type http://nokola.com/SampleService/ServiceClientAppTestPage.aspx I got the above error message.


One Way to Fix It


I’ve posted the not working code of the WCF Service here: http://www.nokola.com/sources/ServiceClientApp.zip


 


Let’s fix it!


 


The fix that I will apply is to create a second .svc file and create a custom factory for the non-www address in it. Then I’ll programmatically call one service or the other from within the Silverlight Client App, based on what address the user typed in their browser.


 


Step 1: Create ServiceNoWww.svc


Copy-paste in Solution Explorer Service.svc and rename it to ServiceNoWww.svc


 


Change its factory specification to:


Factory=”MyServiceHostFactoryNoWww


Step 2: Create custom factory for the ServiceNoWww.svc in code behind


Open App_Code/Service.cs and add (don’t change the existing one) this text:


 


public class MyServiceHostFactoryNoWww : ServiceHostFactory {


    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) {


        return new CustomServiceHost(serviceType, new Uri(“http://nokola.com/SampleService/Service.svc”));


    }


}


 


Note that I removed the www. before nokola.com and named the class same using the same name specified in ServiceNoWww.svc


 


Step 3: Change the client app to switch between the two services transparently


 


Replace the service instantiation code in Page.xaml.cs.


 


Original:


client = new ServiceClientApp.ServiceReference1.ServiceClient();


 


 


Changed:


Binding defaultBinding = new System.ServiceModel.BasicHttpBinding();


EndpointAddress defaultAddress;


if (HtmlPage.Document.DocumentUri.AbsoluteUri.ToLower().Contains(“www.”))


{


    defaultAddress = new System.ServiceModel.EndpointAddress(“http://www.nokola.com/SampleService/Service.svc”);


}


else


{


    defaultAddress = new System.ServiceModel.EndpointAddress(“http://nokola.com/SampleService/Service.svc”);


}


client = new ServiceClientApp.ServiceReference1.ServiceClient(defaultBinding, defaultAddress);


            }


            client = new ServiceClientApp.ServiceReference1.ServiceClient(defaultBinding, defaultAddress);


 


Note: you don’t have to add reference to the second service in your app, only change the default address using the above code.

Comments (0)