Friday, July 25, 2008

Hosting a WCF Service on an IIS site with Multiple Bindings

If you ever tried hosting a WCF Service on an IIS site with Multiple Bindings you will be familiar with the error "This collection already contains an address with scheme http...". The workarounds you can find on the internet and the recent introduction of baseAddressPrefixFilters in WCF can help get passed this error, but they won't allow you to receive requests on all the configured bindings which is usually what people I talked to actually want to do. So here's how you would go by making your service accessible from all available bindings:

1) Use a custom Factory, here's what this looks like in your .svc file:

<%@ ServiceHost Language="C#" Debug="true" Service="MyService.MyService"
    Factory="MyService.MultipleHostsFactory" %>

2) The implementation, rather than selecting a single address from the list that is passed in, overrides this with a completely empty list:

class MultipleHostsFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return base.CreateServiceHost(serviceType, new Uri[] { });
    }
}

3) Your web.config will now need to specify the full list of absolute addresses you're effectively listening to. This needs to exactly match your IIS configuration or this won't work:

<configuration>
  <system.serviceModel>
    <services>
      <service name="MyService.MyService">
        <endpoint address="http://hiring.contoso.com/WcfMultipleHosts/test.svc" binding="basicHttpBinding" contract="MyService.IServiceContract" />
        <endpoint address="http://hiring.contoso.com.uk/WcfMultipleHosts/test.svc" binding="basicHttpBinding" contract="MyService.IServiceContract" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Done.

Be aware that anything that relies on base addresses will now also need to be converted to explicit full addresses, here's an example of how you'd enable the help page for httpGet and enable service metadata:

<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="diagnose">
          <serviceMetadata httpGetEnabled="true" httpGetUrl="http://hiring.contoso.com/WcfMultipleHosts/test.svc" httpsGetEnabled="false" />
          <serviceDebug httpHelpPageUrl="http://hiring.contoso.com.uk/WcfMultipleHosts/test.svc" httpsHelpPageEnabled="false" includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="diagnose" name="MyService.MyService">
        <endpoint address="http://hiring.contoso.com/WcfMultipleHosts/test.svc" binding="basicHttpBinding" contract="MyService.IServiceContract" />
        <endpoint address="http://hiring.contoso.com.uk/WcfMultipleHosts/test.svc" binding="basicHttpBinding" contract="MyService.IServiceContract" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

5 comments:

baggio1510 said...

But what about if I have multiple identities .. and I mean 3 identities. I have dataservice.service1.com/Service.svc which is the base address and then I have dataservice.service2.com and dataservice.service3.com that I all want to default to the same WCF service given by the original service1 address. How can I get that to work? Nothing on the net I've tried works at all. BaseAddressFilterPrefix does not work, picking a url from the overridden CreateHost (or leaving it blank) does not work. I am stuck!!!!

ranamauro said...

this technique allows you to map the same service to all three addresses: dataservice.service1.com/Service.svc,dataservice.service2.com/Service.svc and dataservice.service3.com/Service.svc. is that is not what you want, can you clarify exactly how you'd like it to work? also, it might help to see what you've tried so far and how it's failing.

dwhite said...

I'm running into issues using this techinique. I'm using IIS 6 and .NET 3.5. I have posted my full problem on MSDN: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/9954c2f6-b081-424b-a164-1b98c7277873. If you have any suggestions it would be helpful.

-Damien

Serializer said...

What do you mean specifically when you say:

"This needs to exactly match your IIS configuration or this won't work:"

Do you mean I have to configure each service separately in IIS, as well as my main web site?

The problem is, I'm running in a shared hosting environment so I don't have access to IIS configuration.

All I'm trying to do is make my service work from both http://mysite.com and http://www.mysite.com . Should be a simple task? However I've tried your method and now I get an exception on the return line of the MultipleHostsFactory stating: "Could not find a base address that matches scheme http for the endpoint with binding MetadataExchangeHttpBinding. Registered base address schemes are []."

Any help appreciated. Thanks!

tonywhalen said...

Thanks for this post. It just saved me from hours of pain. All I wanted was to support www.mydomain.co.uk and mydomain.co.uk on the same IIS6 Web Site and this technique was the only way I could get it to work. Thanks again