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:

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

 

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

· https://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:

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

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

https://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("https://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: https://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 https://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: https://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("https://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("https://www.nokola.com/SampleService/Service.svc");

}

else

{

    defaultAddress = new System.ServiceModel.EndpointAddress("https://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.