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.
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>
The issue is not with the config file but with the IIS setup.
You need to enable both HTTP & HTTPS in IIS.
In IIS 7.5, go to your site and click on Bindings under Edit Site Action. Ensure that both http & https have been added.
Then you need to create a binding for HTTP under <basicHttpBinding>
, with security mode set to none.
Add the newly created binding configuration to http endpoint.
You are good to go. Let me know if you need further issue.
The solution for running in a local domain and also running on production and other environments, without relying on memory to change anything, is config transformations. They transform the compiled web.config based on the selected configuration profile. Locally I run in Debug
mode, on the testing environment I publish to a TestRelease
profile, and production I have another profile:
If you can't expand your web.config, you can right click and add config tranforms. In order to get more than Debug and Release, you add more configurations via the manager:
Here's example transformations:
Web.Debug.config
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--...-->
<system.serviceModel>
<protocolMapping>
<add binding="basicHttpBinding" scheme="http" xdt:Transform="SetAttributes" />
</protocolMapping>
<bindings>
<basicHttpBinding>
<binding xdt:Locator="Match(name)" name="basicHttpBindingConfiguration">
<security xdt:Transform="Remove">
<transport xdt:Transform="Remove"/>
</security>
</binding>
<binding xdt:Locator="Match(name)" name="fileTransferBinding">
<security xdt:Transform="Remove">
<transport xdt:Transform="Remove"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
Web.Release.config
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--...-->
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" xdt:Transform="Replace"/>
<serviceDebug includeExceptionDetailInFaults="false" xdt:Transform="Replace"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" xdt:Transform="Replace"/>
</protocolMapping>
<bindings>
<basicHttpBinding>
<binding xdt:Locator="Match(name)" name="basicHttpBindingConfiguration">
<security mode="Transport" xdt:Transform="Insert">
<transport clientCredentialType="None" proxyCredentialType="None" />
</security>
</binding>
<binding xdt:Locator="Match(name)" name="fileTransferBinding">
<security mode="Transport" xdt:Transform="Insert">
<transport clientCredentialType="None" proxyCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<directoryBrowse enabled="false" xdt:Transform="Replace"/>
</system.webServer>
</configuration>