六 WCF4.0中的簡化配置功能
WCF4.0為了簡化服務配置,提供了默認的終結點、綁定和服務行為。也就是說,在開發WCF服務程序的時候,即使我們不提供顯示的 服務終結點,WCF框架也能為我們的服務提供一些默認配置功能的服務終結點。當然也包含默認的綁定和默認的服務行為。這一切都是為了簡化配置過程,避免一 些不必要的錯誤。
下面我們就來通過代碼示例來體驗一下WCF4.0提供簡化配置的功能。
(1)默認終結點
默認終結點(Default Endpoints)指的是,如果開發人員沒有為服務顯示配置服務終結點(Endpoints)。WCF4.0會根據已有的基地址,產生針對每個基地址一個默認的終結點。
我們在前面的示例之中,都是以標准的WCF服務配置進行配置文件的書寫,明確提供了Address、Binding和Contract,也就是ABC,完整代碼見之前的示例。終結點配置代碼如下:
<service name="WCFService.WCFService"> <endpoint name="endpointService" address="http://localhost:8000/WCFService" binding="wsHttpBinding" contract="WCFService.IWCFService"> </endpoint> </service>
啟動之后生成的地址如下圖。
現在我們在配置文件中的<host>節點中的<baseAddress>節點添加基地址,那么在<service>節點中的<address>可以使用相對地址。不過這個配置寫法只能在WCF4.0之后的版本中使用,只要你提供了一個服務的基地址,即使沒有服務終結點配置,WCF框架會自動根據基地址創建一個默認的終結點,也就是Default Endpoint。請看下面的服務配置信息:
<service name="WCFService.WCFService"> <host> <baseAddresses> <add baseAddress="http://localhost:8000/"/> <add baseAddress="http://localhost:8080/"/> </baseAddresses> </host> </service>
不過這樣配置,程序運行時會報錯,錯誤信息如下:
其他信息: 此集合已經采用方案 http 的地址。此集合中每個方案中最多只能包含一個地址。如果服務承載於 IIS 中,則可以通過將“system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled”設置為 true,或指定“system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters”來解決此問題。
正確的配置方式如下:
<service name="WCFService.WCFService"> <host> <baseAddresses> <add baseAddress="http://localhost:8000/"/> <add baseAddress="net.tcp://localhost:8080/"/> </baseAddresses> </host> </service>
一般WCF4.0里我們只指定一個baseAddress,做為示例,我在這里指定了2個服務的baseAddress。那么按照理論,WCF框架會為我們產生2個默認的終結點。運行代碼結果如下圖:
這里我們能看到根據已有的兩個baseAddress,WCF框架產生了2個默認服務終結點。如果我們把2個基地址也去除,運行WCF Host程序,就會出現沒有終結點的錯誤。這里針對每個不同的基地址,WCF4.0提供了不同的默認綁定。如上圖中的,第一個終結點是使用Http協議,WCF 4.0框架提供的是 basicHttpBinding。第二個終點使用的是net.tcp協議,WCF 4.0框架提供的是netTcpBinding。
(2)指定終結點
如果我們顯示指定服務的終結點,那么WCF就不會基於baseAddress產生新的默認的終結點。修改配置代碼如下:
<services> <service name="WCFService.WCFService" > <endpoint name="endpointService" address="http://localhost:8100/WCFService" binding="basicHttpBinding" contract="WCFService.IWCFService"> </endpoint> <!--<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />--> <host> <baseAddresses> <add baseAddress="http://localhost:8000/"/> <add baseAddress="net.tcp://localhost:8080/"/> </baseAddresses> </host> </service> </services>
這時在運行WCF Host程序,就不會產生新的默認的終結點。運行結果如下圖:
(3)強制為每個基地址添加默認終結點
如果我們想為每個基地址baseAddress, 保留產生的默認的服務終結點,還想添加自己的服務終結點怎么辦呢?因為之前WCF服務提供了顯示的終結點以后,默認服務終結點就消失了。這個時候我們可以 通過調用Host的AddDefaultEndpoints()方法實現這一目的。此方法的目的會為服務里每個基地址產生一個終結點。綁定也是使用默認的 配置。代碼就是:host.AddDefaultEndpoints();新的運行結果如下,這里理論上,我們會得到3個服務終結點:2個默認+1個顯示 配置。運行程序結果如下圖:
(4) 綁定協議映射:
默認協議映射(Default Protocol Mapping)。WCF提供針對綁定協議的映射機制,用來簡化配置。我們可以在服務配置里指定某個地址結構映射到特定的綁定上,WCF會自動在服務里更新匹配該地址結構的默認終結點的綁定為映射的綁定。
這里把這個概念放到此處,根據上面的例子來解釋,很簡單。要使用綁定映射功能。我需要在服務里指定使用的映射。配置文件里的 <protocolMapping>節點,我們可以在其內部指定此解決方案里使用的服務的綁定映射。配置代碼如下:
<system.serviceModel> <protocolMapping> <add scheme="http" binding="basicHttpBinding"/> <add scheme="net.tcp" binding="netTcpBinding"/> <add scheme="net.pipe" binding="netNamedPipeBinding"/> <add scheme="net.msmq" binding="netMsmqBinding"/> </protocolMapping>
修改后的配置代碼如下:
<system.serviceModel> <protocolMapping> <add scheme="http" binding="webHttpBinding"/> <add scheme="net.tcp" binding="netTcpBinding"/> <add scheme="net.pipe" binding="netNamedPipeBinding"/> <add scheme="net.msmq" binding="netMsmqBinding"/> </protocolMapping> </system.serviceModel>
注意上圖運行結果,基於http基地址產生的默認終結點,使用的綁定是basicHttpBinding。我們這里為了測試,把http的映射修改為webHttpBinding,然后啟動Host程序,再來觀察一下結果。運行結果如下圖:
觀察上圖中的第二個終結點,綁定已經變為webHttpBinding。
配置文件:
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true"/> </system.web> <!-- When deploying the service library project, the content of the config file must be added to the host's app.config file. System.Configuration does not support config files for libraries. --> <system.serviceModel> <protocolMapping> <add scheme="http" binding="basicHttpBinding"/> <add scheme="net.tcp" binding="netTcpBinding"/> <add scheme="net.pipe" binding="netNamedPipeBinding"/> <add scheme="net.msmq" binding="netMsmqBinding"/> </protocolMapping> <services> <service name="WCFService.WCFService" > <endpoint name="endpointService" address="http://localhost:8100/WCFService" binding="basicHttpBinding" contract="WCFService.IWCFService"> </endpoint> <!--<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />--> <host> <baseAddresses> <add baseAddress="http://localhost:8000/"/> <add baseAddress="net.tcp://localhost:8080/"/> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors > <behavior > <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> <bindings> <basicHttpBinding></basicHttpBinding> <basicHttpContextBinding></basicHttpContextBinding> <netMsmqBinding></netMsmqBinding> <netNamedPipeBinding></netNamedPipeBinding> <webHttpBinding></webHttpBinding> <wsHttpBinding> <binding > <security mode="Message"> <transport clientCredentialType="None"> </transport> <message clientCredentialType="Certificate"/> </security> </binding> </wsHttpBinding> <wsDualHttpBinding></wsDualHttpBinding> </bindings> </system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration>
代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Description; using System.Runtime.Serialization; namespace WCFHost { //采用自托管方式,也可以是IIS、WAS,Windows服務等用戶自定義程序托管服務 //Simple Configration public class WCFHost { static void Main(string[] args) { //反射方式創建服務實例, ServiceHost host = new ServiceHost(typeof(WCFService.WCFService));//, //為WCF服務中的每個基地址生成一個終結點 host.AddDefaultEndpoints(); //判斷是否以及打開連接,如果尚未打開,就打開偵聽端口 if (host.State !=CommunicationState.Opening) host.Open(); //顯示運行狀態 Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("WCF 服務寄宿程序開始運行! WCF 服務狀態 {0}",host.State); //print endpoint information Console.WriteLine("Endpoints count is {0}", host.Description.Endpoints.Count); Console.ForegroundColor = ConsoleColor.Yellow; foreach (ServiceEndpoint se in host.Description.Endpoints) { Console.WriteLine("[終結點]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-綁定]: {2} \r\n\t [C-協定]: {3}", se.Name,se.Address, se.Binding.Name, se.Contract.Name); } //等待輸入即停止服務 Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Press any key to stop the service."); Console.ReadLine(); host.Close(); } } }
(5) 默認綁定配置:
在WCF3.0或3.5中,您需要為您的每一個綁定取一個名字,然后您的每個終結點需要指定某個定義好的綁定來進行應用。WCF4.0 引入了默認綁定配置(Default Binding Configurations)。我們可以提供某種類型的綁定的配置,但是如果不把這個綁定配置應用到特定的服務終結點上。WCF會默認把所有匹配該綁定的終結點,全部應用對應的綁定配置。
例如,我們為wsHttpBinding啟用了消息安全模式,並且客戶端認證方式為證書,一般的做法是我們定義個綁定配置。代碼如下:
<wsHttpBinding> <binding name="wsHttpBindingSecurity"> <security mode="Message"> <transport clientCredentialType="None"> </transport> <message clientCredentialType="Certificate"/> </security> </binding> </wsHttpBinding>
然后在把這個綁定配置應用到特定的服務終結點上,例如:
<endpoint name="endpointService" address="http://localhost:8100/WCFService" binding="wsHttpBinding" contract="WCFService.IWCFService"> </endpoint>
這樣才能對此終結點啟用綁定配置的安全要求。但是現在在新的WCF機制下。我們不需要把配置應用到默認的終結點上。簡單的配置如下:
<wsHttpBinding> <binding > <security mode="Message"> <transport clientCredentialType="None"> </transport> <message clientCredentialType="Certificate"/> </security> </binding> </wsHttpBinding>
這些配置會作用到匹配wsHttpBinding的默認的服務終結點上。
由於沒有證書,所以會出現以下錯誤信息:
其他信息: “http://localhost:8100/WCFService”處帶有協定“"IWCFService"”的 ChannelDispatcher 無法打開其 IchannelListener。
這個錯誤的解決方式,需要如果將mode改為message方式,這種方式是對消息加密或簽名,因此必須要注定證書
<behaviors> <serviceBehaviors > <behavior > <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false"/> <serviceCredentials> <serviceCertificate findValue="e6 00 c8 f6 93 ce 5c 8f a6 08 a5 c8 0e 09 de 5e 8b 9d 3a 99" x509FindType="FindByThumbprint" storeLocation="CurrentUser" storeName="My" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors>
然后啟動Host程序,再來觀察一下結果。運行結果如下圖:
(6)默認行為配置:
在WCF3.0或3.5中,您需要為您的每一個行為取一個名字,然后您的每個終結點需要指定某個定義好的行為來進行應用。可以想象,當您有幾十個甚至幾百個服務同時應用相同的綁定或行為的時候,指定這些名字將成為單純的體力勞動。為了避免這樣的情況,WCF4.0 引入了默認行為配置(Default Behavior Configurations)。WCF允許你定義一個服務行為,該服務行為會默認匹配到該運行在此機器上的全部該解決方案的全部服務(或者終結點)。
現在在WCF里,以服務行為為例,我們這里的定義不包含名稱。配置代碼如下:
<serviceBehaviors> <behavior > <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors>
服務的配置非常簡單,我們這里只使用一個baseAddress。配置信息如下:
<services> <service name="WCFService.WCFService" > <host> <baseAddresses> <add baseAddress="http://localhost:8000/"/> </baseAddresses> </host> </service> </services>
這里我們也就啟用了服務元數據交換,為了檢查這個默認服務behavior設置,會不會作用到我們的服務上,現在我們來運行Host,然后在瀏覽器里查詢服務元數據。在WCF3.5里不顯示配置,應該是不可以查看的元數據的。這里我們打開瀏覽器,輸入元數據地址,查看信息如下:
默認情況下,這個配置會對所有的服務或終結點起作用。如果我們把配置信息放置在machine.config,則會作用於機器上所有托管的服務或者終結點。如果是app.config,則只影響本托管程序里的服務行為。
最后的配置文件如下:
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true"/> </system.web> <!-- When deploying the service library project, the content of the config file must be added to the host's app.config file. System.Configuration does not support config files for libraries. --> <system.serviceModel> <protocolMapping> <add scheme="http" binding="basicHttpBinding"/> <add scheme="net.tcp" binding="netTcpBinding"/> <add scheme="net.pipe" binding="netNamedPipeBinding"/> <add scheme="net.msmq" binding="netMsmqBinding"/> </protocolMapping> <services> <service name="WCFService.WCFService" > <endpoint name="endpointService" address="http://localhost:8100/WCFService" binding="wsHttpBinding" contract="WCFService.IWCFService"> </endpoint> <!--<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />--> <host> <baseAddresses> <add baseAddress="http://localhost:8000/"/> <add baseAddress="net.tcp://localhost:8080/"/> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors > <behavior > <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false"/> <serviceCredentials> <serviceCertificate findValue="e6 00 c8 f6 93 ce 5c 8f a6 08 a5 c8 0e 09 de 5e 8b 9d 3a 99" x509FindType="FindByThumbprint" storeLocation="CurrentUser" storeName="My" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <bindings> <basicHttpBinding></basicHttpBinding> <basicHttpContextBinding></basicHttpContextBinding> <netMsmqBinding></netMsmqBinding> <netNamedPipeBinding></netNamedPipeBinding> <webHttpBinding></webHttpBinding> <wsHttpBinding> <binding > <security mode="Message"> <transport clientCredentialType="None"> </transport> <message clientCredentialType="Certificate"/> </security> </binding> </wsHttpBinding> <wsDualHttpBinding></wsDualHttpBinding> </bindings> </system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration>