How can I combine the WCF services config for both http and https in one web.config?

sohtimsso1970 picture sohtimsso1970 · Dec 3, 2010 · Viewed 34.7k times · Source

I spent a lot of time figuring out how to configure my WCF services so that they would work for https in the production environment.

Basically, I needed to do this:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
  <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
    <endpoint address="" bindingNamespace="https://secure.mydomain.com" binding="basicHttpBinding" bindingConfiguration="HttpsBinding" contract="MyNamespace.IMyService"/>
  </service>
</services>
<bindings>
  <basicHttpBinding>
    <binding name="HttpsBinding">
      <security mode="Transport">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

Adding the bindingNamespace attribute to the endpoint is the final thing that made it work.

But this config doesn't work in my local dev environment where I'm working under regular http. So my config there is:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
  <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
    <endpoint address="" binding="basicHttpBinding" contract="MyNamespace.IMyService"/>
  </service>
</services>

The differences here are that I've set the httpsGetEnabled attribute to false, and I removed the bindingConfiguration and bindingNamespace.

The problem is: how do I create one config block that handles BOTH?

I really hate having to make lots of special modifications to the config every time I do a release. Yes, I know I could have a post-build task that automatically changes the values, but I'd like to merge the configs if possible.

I tried something like this:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
  <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
    <endpoint address="" binding="basicHttpBinding" contract="MyNamespace.IMyService"/>
    <endpoint address="" bindingNamespace="https://secure.mydomain.com" binding="basicHttpBinding" bindingConfiguration="HttpsBinding" contract="MyNamespace.IMyService"/>
  </service>
</services>
<bindings>
  <basicHttpBinding>
    <binding name="HttpsBinding">
      <security mode="Transport">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

I figured that putting both endpoints would give it two options to look for when activating the service. However, this doesn't work. I get this error:

Could not find a base address that matches scheme https for the endpoint with binding BasicHttpBinding. Registered base address schemes are [http].

From looking around SO and the rest of the internet, it appears that others have had problems slaying this dragon.

Answer

marc_s picture marc_s · Dec 3, 2010

Well, one problem with your combined config is that your two endpoints are on the same address - that won't work.

If you're hosting in IIS, then your server, virtual directory and the *.svc file needed will determine your basic address - it'll be something like:

http://yourservername/VirtualDirectory/YourService.svc

If you want to have two endpoints, at least one of them needs to define a relative address:

<services>
    <service name="MyNamespace.MyService" 
             behaviorConfiguration="MyServiceBehavior">
       <endpoint 
           address="basic" 
           binding="basicHttpBinding" 
           contract="MyNamespace.IMyService"/>
       <endpoint 
           address="secure" 
           binding="basicHttpBinding" bindingConfiguration="HttpsBinding"  
           contract="MyNamespace.IMyService"/>
    </service>
</services>

In this case, you'd have your HTTP endpoint on:

http://yourservername/VirtualDirectory/YourService.svc/basic

and your secure HTTPS endpoint on:

https://yourservername/VirtualDirectory/YourService.svc/secure

Furthermore: your secure endpoint uses a HttpsBinding configuration - but you're lacking such a binding configuration - all you have is:

<bindings>
  <basicHttpBinding>
    <binding name="HttpBinding">
      <security mode="None">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

You need to add the HttpsBinding configuration!!

<bindings>
  <basicHttpBinding>
    <binding name="HttpBinding">
      <security mode="None">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
    <binding name="HttpsBinding">
      <security mode="Transport">
          <transport clientCredentialType="Windows" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>