Having worked with WCF Web services for some time now, I have been presented with many of the caveats surrounding the technology. All in all, WCF is a tremendous platform, but hosting WCF Web services on IIS websites that have multiple host headers seems to be one commonly recurring pain point.
Hosting WCF Web services through IIS is one of the more common host environments for any software written using the WCF stack. Many of you have probably seen or heard of the following error when hosting a WCF Web service (*.svc ):
This collection already contains an address with scheme http. There can be at most one address per scheme in this collection. Parameter name: item

WCF determines its base address for Web services by examining the host bindings for the site from IIS. The service host will only allow a single base address ber scheme, and if the host finds more than one base address for the scheme (http in this instance), the host will throw the above error. Many have stated that this is a clear defect in WCF and to some extent, it’s hard to disagree. Binding a website to multiple host headers is an extremely common thing and WCF should facilitate this. Luckily for us, WCF does allow us to (slightly) remedy this situation.
Test Case:
For our scenario, we will be hosting a service that will return the 5 most recent tweets from the @apterasoftware Twitter user.
- Configure your IIS website to use two or more host header bindings. In our example, local.apterasoftware.com and local.apterainc.com
- Use ASP.NET compatibility mode for the host environment. This is important because we are going to use the ASP.NET runtime to examine the request URL to create a base address for our service host at runtime. This must be declared in the Web.config and since we are also going to use this service from ASP.NET AJAX, the attribute must also decorate the class definition
Web.config
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<!-- removed for brevity -->
</system.serviceModel>
Class Definition
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class TwitterService
: ITwitterService
{
/* Implementation removed for brevity */
} - Create a service host factory that derives from the WCF ServiceHostFactory. This service host factory will examine the current request URL and create hosts using the appropriate base address
public class MultipleHeaderServiceHostFactory
: ServiceHostFactory
{
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
return base.CreateServiceHost(
constructorString,
this.FilterBaseAddresses(baseAddresses));
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return base.CreateServiceHost(
serviceType,
this.FilterBaseAddresses(baseAddresses));
}
private Uri[] FilterBaseAddresses(Uri[] baseAddresses)
{
Uri[] retVal = null;
foreach (var item in baseAddresses)
{
/* Find the appropriate host. We could also use LINQ to
* query for the item rather than iterate the array */
if (string.Compare(item.Host, HttpContext.Current.Request.Url.Host, true) == 0)
{
retVal = new Uri[1];
retVal[0] = item;
break;
}
}
return retVal;
}
} - Set the Factory attribute in the .svc file to use our custom factory for service activation
<%@ ServiceHost
Language="C#"
Debug="true"
Service="Aptera.Samples.WcfBindings.Web.UI.TwitterService"
CodeBehind="TwitterService.svc.cs"
Factory="Aptera.Samples.WcfBindings.Services.MultipleHeaderServiceHostFactory" %>
One important note: while this now allows you to host a WCF Web service from a site with multiple host header bindings, it does not actually make the service available from URLs containing all of the bound host names. WCF will create an instance of the service host on the first request, so the service host will be created with the URL requested first serving as the base address. In other words, if I have http://local.apterainc.com/Service.svc and http://local.apterasoftware.com/Service.svc and I request the first URL, local.apterainc.com will be the base address for the service host. This doesn’t make the base address dynamic on a per request basis; however it does allow us to have a little more flexibility on creating the base address for our service host.
Extending to ASP.NET AJAX
ASP.NET AJAX uses Web services to deliver JSON data to AJAX clients. This is performed through traditional ASMX Web services as well as WCF Web services. Unfortunately, the same issue applies if we are consuming this service from ASP.NET AJAX. To remedy this issue, we can perform one of the following:
- Use the configuration-less method of WCF Web service activation for AJAX. This is accomplished by declaratively setting which factory to use when creating the service host for the service (step 4 from above). This will require us to create another service host as we did earlier (step 3), however the base class will now be WebScriptServiceHostFactory. This is the host factory used to activate services for AJAX consumption without any configuration in the Web.config.
- Create an additional endpoint on the same service for AJAX. Although we must specify a configuration for this option, it allows us to use the same service host factory from the first demonstration, and enable it for Web scripting. Ideally, you would not use the same Web service implementation for AJAX and general consumption, but this example will demonstrate how to do just that, should your application require this.
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior name="TwitterServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="AjaxServiceBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="Aptera.Samples.WcfBindings.Web.UI.TwitterService"
behaviorConfiguration="TwitterServiceBehavior">
<endpoint address=""
binding="basicHttpBinding"
contract="Aptera.Samples.WcfBindings.Services.ITwitterService" />
<endpoint address="js"
behaviorConfiguration="AjaxServiceBehavior"
binding="webHttpBinding"
contract="Aptera.Samples.WcfBindings.Services.ITwitterService" />
</service>
</services>
</system.serviceModel> We can then reference the endpoint from our ScriptManager:
<asp:ScriptManager ID="scriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/TwitterService.svc/js" />
</Services>
</asp:ScriptManager>
That's it. You now have a few options for hosting a WCF service on a site with multiple host header bindings as well as options for extending this to ASP.NET AJAX, with or without using a configuration in the Web.config. Feel free to download the solution for this example.