WCF: WS-* HTTP Binding (WSHttpBinding)

One of the most popular system-provided WCF bindings is WS-* binding, Commonly referred to as WSHttpBinding, this binding provides a secure and reliable approach.The binding is useful where:

  • You suspect software, system, or network failures may hamper the communication between clients and your WCF service.
  • You need basic security, such as encryption and authentication.

The binding implements the following specifications:

Messages are encoded as Text/XML and are transported via HTTP. Note that WSHttpBinding is only for non-duplex suitable service contracts.

Also, know that both service and client must use WSHttpBinding. One cannot use BasicHttpBinding while the other uses WSHttpBinding even if no security is used and the encoding is the same. The disagreement between the two bindings arise from different versions of SOAP being used.

Updating the Service to Support WSHttpBinding
If all goes well, then all we have to do to switch from BasicHttpBinding to WSHttpBinding is change the .NET configuration file.

Here's our original App.config file,  with the parts we're about to change highlighted:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  
  <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>

  <system.serviceModel>
    <services>
      <service name="MyWcfService.MyService" behaviorConfiguration="BASIC">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:2374/MyService.svc"/>
          </baseAddresses>
        </host>
        <endpoint
          address="BASIC"
          binding="basicHttpBinding"
          bindingConfiguration="basicBindingConfiguration"
          contract="MyWcfService.IMyService"/>
        <endpoint
          address="MEX"
          binding="mexHttpBinding"
          contract="IMetadataExchange"/>
      </service>
    </services>

    <bindings>
      <basicHttpBinding>
        <binding name="basicBindingConfiguration">
          <security mode="None"/>
        </binding>
      </basicHttpBinding>
    </bindings>
    
    <behaviors>
      <serviceBehaviors>
        <behavior name="BASIC">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>
  
  <log4net>
    <appender name="FileAppender" type="log4net.Appender.FileAppender">
      <file value="MyWcfService-Host.log" />
      <appendToFile value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <param name="Header" value="[Header]\r\n" />
        <param name="Footer" value="[Footer]\r\n" />
        <param name="ConversionPattern" value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="FileAppender" />
      <appender-ref ref="ConsoleAppender"/>
    </root>
  </log4net>
</configuration>

And here's the revised version of the App.config file, the updated lines highlighted:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  
  <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>

  <system.serviceModel>
    <services>
      <service name="MyWcfService.MyService" behaviorConfiguration="BASIC">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:2374/MyService.svc"/>
          </baseAddresses>
        </host>
        <endpoint
          address="BASIC"
          binding="wsHttpBinding"
          bindingConfiguration="wsBindingConfiguration"
          contract="MyWcfService.IMyService"/>
        <endpoint
          address="MEX"
          binding="mexHttpBinding"
          contract="IMetadataExchange"/>
      </service>
    </services>

    <bindings>
      <basicHttpBinding>
        <binding name="basicBindingConfiguration">
          <security mode="None"/>
        </binding>
      </basicHttpBinding>
      <wsHttpBinding>
        <binding name="wsBindingConfiguration">
          <security mode="None"/>
        </binding>
      </wsHttpBinding>
    </bindings>
    
    <behaviors>
      <serviceBehaviors>
        <behavior name="BASIC">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>
  
  <log4net>
    <appender name="FileAppender" type="log4net.Appender.FileAppender">
      <file value="MyWcfService-Host.log" />
      <appendToFile value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <param name="Header" value="[Header]\r\n" />
        <param name="Footer" value="[Footer]\r\n" />
        <param name="ConversionPattern" value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="FileAppender" />
      <appender-ref ref="ConsoleAppender"/>
    </root>
  </log4net>
</configuration>

You can see that I left the old binding in the configuration file just in case I ever want to go back to it. The new binding will be used because I tell the service endpoint to use the binding configuration named "wsBindingConfiguration".

Run the service and we get the following, showing that with only a minor change to only the configuration file, our service still performs:


Updating the Client to Support WSHttpBinding
As with the service, only the configuration file needs to be changed. Below is our original App.config file with the important parts highlighted:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  
  <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>

  <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IMyService" />
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint 
              address="http://localhost:2374/MyService.svc/BASIC"
              binding="basicHttpBinding" 
              bindingConfiguration="BasicHttpBinding_IMyService"
              contract="IMyService" 
              name="BasicHttpBinding_IMyService" />
        </client>
    </system.serviceModel>
  
  <log4net>
    <appender name="FileAppender" type="log4net.Appender.FileAppender">
      <file value="Log4NetApplicationLog.log" />
      <appendToFile value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <param name="Header" value="[Header]\r\n" />
        <param name="Footer" value="[Footer]\r\n" />
        <param name="ConversionPattern" value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="FileAppender" />
      <appender-ref ref="ConsoleAppender"/>
    </root>
  </log4net>
</configuration>

And here's the revised configuration file; again, the changes are highlighted:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  
  <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>

  <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IMyService" />
            </basicHttpBinding>
            <wsHttpBinding>
              <binding name="WSHttpBinding_IMyService">
                <security mode="None"/>
              </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint 
              address="http://localhost:2374/MyService.svc/BASIC"
              binding="wsHttpBinding"
              bindingConfiguration="WSHttpBinding_IMyService"
              contract="IMyService" 
              name="WSHttpBinding_IMyService" />
        </client>
    </system.serviceModel>
  
  <log4net>
    <appender name="FileAppender" type="log4net.Appender.FileAppender">
      <file value="Log4NetApplicationLog.log" />
      <appendToFile value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <param name="Header" value="[Header]\r\n" />
        <param name="Footer" value="[Footer]\r\n" />
        <param name="ConversionPattern" value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="FileAppender" />
      <appender-ref ref="ConsoleAppender"/>
    </root>
  </log4net>
</configuration>

Note that we even kept the old binding, named BasicHttpBinding_IMyService, around and merely pointed our client endpoint to the new one we added. And, even though we changed the name of our client endpoint, it wasn't entirely necessary. (I did so simply to maintain the naming conventions I chose.)

If you left your service running, then great. If not, then fire it up. Next fire up the client with the revised configuration file. You should see the following if you follow the prompts (but don't exit the client):



Voila! With only changes to the configuration file, our client is able to use a new binding to talk to our service, which is also using the new binding.

A Word About Security
One of the biggest reasons to switch bindings is security -- some bindings simply provide access to better security or more security options than others.

We will look at security later but for now we'll concentrate on simply getting examples working of each binding provided by .NET.

What's Next
We'll continue to work our way through the remaining system-provided bindings.

After that, we'll look at why you would choose one over the others, using various criteria (security being only one). Next, we'll dive deeper into security. And, finally, we'll use what we've learned about WCF to stub out some interesting projects.

Comments

Popular posts from this blog

Using Reference Aliases

List of Visual Studio Keyboard Shortcuts