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.
Starting with . NET Framework 4, WCF comes with a new default configuration model that simplifies WCF configuration requirements. If you do not provide any WCF configuration for a particular service, the runtime automatically configures your service with default endpoints, bindings, and behaviors.
Add a new WebHttpBinding configuration that has security mode set to Transport . Assign that new WebHttpBinding configuration to the your Service Endpoint binding. Make sure that your RESTful service can only be accessed via HTTPS by setting httpGetEnabled="false" . Set up the metadata publishing endpoint to use HTTPS.
When configuring a service in Visual Studio, use either a Web. config file or an App. config file to specify the settings. The choice of the configuration file name is determined by the hosting environment you choose for the service.
configuration.svcinfo: Contains a snapshot of the configuration generated for the client service endpoint for the local (app|web).config. configuration91. svcinfo: For each property in config, contains an XPath to the setting and the original value stored in config.
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>
I recently had to make a WCF 3.5 REST (webHttpBinding
) service available on both HTTP and HTTPS in a Microsoft Azure App Service (IIS). It was a fun... and agonizing adventure. Here are my findings and my web.config
's <system.serviceModel>
:
Note: these notes are for a WCF REST web-service running using *.svc
(@ServiceHost
) files within a minimal ASP.NET 4.7 application (with Global.asax
) within IIS 10 on Windows Server 2016. These notes do not apply to self-hosted WCF services, non-REST WCF services (i.e. SOAP), or platforms older than .NET Framework 4.7. This also does not apply to .NET Core.
<serviceHostingEnvironment>
element is essential, ensure that multipleSiteBindingsEnabled="true"
is set. <baseAddressPrefixFilters>
which are not necessary anymore.<serviceMetadata>
element in your web.config
file at all. <endpoint address=""
attributes - just leave the attribute empty. PATH_INFO
-style endpoints) when hosting WCF in IIS: You must always use relative endpoint addresses for IIS-hosted service endpoints. Supplying a fully-qualified endpoint address (for example, http://localhost/MyService.svc) can lead to errors in the deployment of the service if the endpoint address does not point to the IIS-application that hosts the service exposing the endpoint. Using relative endpoint addresses for hosted services avoids these potential conflicts.
web.config
file tells WCF to accept HTTPS connections when the hosting application (i.e. IIS) does not have HTTPS enabled in its parent website. <services>
element to be omitted and be automatically generated behind-the-scenes, however I didn't have consistent results with RESTful services with dual-HTTP+HTTPS bindings, so I still specify my <services>
manually. Here's my <system.serviceModel>
element from my web.config
file:
<system.serviceModel> <services> <service name="WcfService1.MainService"> <endpoint address="" binding="webHttpBinding" contract="WcfService1.IMainService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingInsecure" /> <endpoint address="" binding="webHttpBinding" contract="WcfService1.IMainService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingSecure" /> </service> <service name="WcfService1.AnotherService"> <endpoint address="" binding="webHttpBinding" contract="WcfService1.IAnotherService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingInsecure" /> <endpoint address="" binding="webHttpBinding" contract="WcfService1.IAnotherService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingSecure" /> </service> <!-- etc... --> </services> <behaviors> <endpointBehaviors> <behavior name="myWebBehavior"> <webHttp /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <protocolMapping> <!-- By default (in machine.config), the 'http' scheme is mapped to 'basicHttpBinding' (SOAP), not 'webHttpBinding' (REST) and the 'https' scheme is not mapped. --> <add binding="webHttpBinding" scheme="https" bindingConfiguration="myWebHttpBindingSecure" /> <add binding="webHttpBinding" scheme="http" bindingConfiguration="myWebHttpBindingInsecure" /> </protocolMapping> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> <bindings> <webHttpBinding> <!-- maxReceivedMessageSize="104857600" is 100MiB --> <binding name="myWebHttpBindingInsecure" maxReceivedMessageSize="104857600" transferMode="Streamed"> <security mode="None" /> </binding> <binding name="myWebHttpBindingSecure" maxReceivedMessageSize="104857600" transferMode="Streamed"> <security mode="Transport" /> </binding> </webHttpBinding> </bindings> </system.serviceModel>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With