一.介绍
WCF4.0为开发者带来了很多新的特性.能与工作流服务高度整合,WCF REST服务更友好.总之WCF4.0在服务端与客户端通信中给我们带来了更多的选项,随下是4.0中的新特性:
1.动态服务和端点发现 2.中间路由模式(路由服务) 3.发现通知 4.简化配置 5.协议绑定和容错处理 6.默认端点 7.REST URI更友好 8.默认协议映射 9.开启了WCF REST服务的帮助页面
在本篇文章我们将探索WCF4.0中令人激动的3个新特性.其他的特性我们将在随下的文章中描述.本篇文章我们将讨论:
1.端点发现 2.默认端点 3.开启了WCF REST服务的帮助页面
二.问题
服务端暴露服务端点,客户端通过端点引用服务,当服务端绑定发生根本性的变化,那么客户端不得不去更新服务.这将是一个很繁重的任务.去解决这个问题,我们用一个叫端点发现或者动态服务的机制.
用技术的话说,WCF4.0支持WS-Discovery标准或者协议.
三.WS-Discovery标准
WS-Discovery是在UDP之上发出SOAP消息的多播协议 WS-Discovery是一个标准,它定义了轻量级的机制基于多播消息发现服务.当它初始化的时候允许服务发送Hello的通知消息,当它从网络中移除的时候发送一个By消息.
客户端通过发送探测消息到服务,服务回应与探测匹配的信息,这样客户端就能找到服务 客户端能够找到改变端口的服务,通过发出一个解析消息到服务,服务回应解析匹配消息
我们能可以说WS-Discovery是基于UDP的多播消息交换.一个客户端通过发信息去找到网络上有效的服务
四.WCF Service Discovery API
WCF提供WCF Discovery API.这个API帮助服务发布以及客户端在网络上找到服务 模型:
托管模式
在托管模式中有一个叫发现代理的集中服务器,服务用来发布,客户端用来检索有效的服务信息.
当一个新的服务开启,它发送一个通知消息给发现代理. 发现代理保存有效的服务信息 当一个客户端搜索服务的时候,它发送一个探测请求到发现代理,发现代理检测是否有相匹配的服务.如果有匹配,那么发现代理发送一个匹配探测响应客户端
客户端用那代理返回的服务信息直接连接服务
点对点模式
没有集中服务器,服务通知和客户请求用多播的方式发送 如果服务配置在启动的时候发送一个Hello的通知,客户端不得不监听这个通知,并且做出相应的处理 当客户端发送一个探测请求,每个服务接收到这个请求后都去检测是否匹配探测请求的条件,如果匹配就回应一个探测匹配消息给客户端,
五.例子
在下面例子中我们创建一个基本的服务和一个客户端,客户端用点对点模式发现服务并使用它. 去运行如下例子你需要安装Visual Studio 201 去实现一个可发现的服务,服务发现行为必须添加到服务主机,发现端点必须添加到特定的监听位置
打开VS2010,选择WCF Service应用程序项目模板创建一个新的项目,删除Visual Studio生成的所有代码 IService1.cs实现如下:
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Runtime.Serialization; 5: using System.ServiceModel; 6: using System.ServiceModel.Web; 7: using System.Text; 8: namespace WcfService1 9: { 10: [ServiceContract] 11: public interface IService1 12: { 13: [OperationContract] 14: string GetMessage(string msg); 15: } 16: }Service1.cs:
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Runtime.Serialization; 5: using System.ServiceModel; 6: using System.ServiceModel.Web; 7: using System.Text; 8: namespace WcfService1 9: { 10: public class Service1 : IService1 11: { 12: public string GetMessage(string msg) 13: { 14: return msg; 15: } 16: } 17: }上面的例子示范了一个简单的服务契约和它的实现 打开Web.config,修改System.ServiceModel元素,效果如下:
1: <<>system.serviceModel> 2: <<>services> 3: <<>service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior"> 4: <<>endpoint address="" binding="wsHttpBinding" contract="WcfService1.IService1"> 5: <<>identity> 6: <<>dns value="localhost"/> 7: identity> 8: endpoint> 9: <<>endpoint name ="udpDiscovery" kind="udpDiscoveryEndpoint" /> 10: <<>endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 11: service> 12: services> 13: <<>behaviors> 14: <<>serviceBehaviors> 15: <<>behavior name="WcfService1.Service1Behavior"> 16: <<>serviceMetadata httpGetEnabled="true"/> 17: <<>serviceDebug includeExceptionDetailInFaults="false"/> 18: <<>serviceDiscovery /> 19: behavior> 20: serviceBehaviors> 21: behaviors> 22: system.serviceModel>1.上面代码中我们添加了一个新的端点 2.新添加的端点的类别是udpDiscoveryEndpoint 3.这个端点用来找到服务 4.服务发现行为也被添加
按下F5运行服务,这个服务将托管在内嵌的ASP.NET WEB服务器中
现在添加一个控制台项目到解决方法,引用System.ServiceModel.Discovery.dll到控制台项目中,添加名称空间System.ServiceModel.Discovery
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using ConsoleApplication1.ServiceReference1; 6: using System.ServiceModel; 7: using System.ServiceModel.Discovery; 8: namespace ConsoleApplication1 9: { 10: class Program 11: { 12: static void Main(string[] args) 13: { 14: DiscoveryClient discoverclient = new DiscoveryClient(new UdpDiscoveryEndpoint()); 15: FindResponse response = discoverclient.Find(new FindCriteria(typeof(IService1))); 16: EndpointAddress address = response.Endpoints[0].Address; 17: Service1Client client = new Service1Client(new WSHttpBinding(), address); 18: string str = client.GetMessage("Hello WCF 4 "); 19: Console.WriteLine(str); 20: Console.ReadKey(true); 21: } 22: 23: } 24: }输出:
六.默认端点和协议映射
在WCF 3.x时代在配置文件中配置端点是一件很繁琐的事情,在WCF4中如果没有配置任何端点,那么会有一个默认端点与服务关联.随下例子将演示默认端点的使用: 首先创建一个service应用程序,然后创建两个契约 IService1.cs:
1: [ServiceContract] 2: public interface IService1 3: { 4: 5: [OperationContract] 6: string GetData(); 7: 8: } IService2.cs: 1: [ServiceContract] 2: public interface IService2 3: { 4: 5: [OperationContract] 6: string GetMessage(); 7: 8: }实现服务:
1: public class Service1 : IService1, IService2 2: { 3: 4: public string GetData() 5: { 6: return "Hello From Iservice1 " ; 7: } 8: public string GetMessage() 9: { 10: 11: return " Hello From Iservice2"; 12: } 13: }我们现在创建一个控制台程序托管,引用服务:
1: ServiceHost host = new ServiceHost(typeof(WcfService1.Service1), 2: new Uri("http://localhost:8080/service"), 3: new Uri("net.tcp://localhost:8081/service")); 我们创建了两个基地址,一个HTTP绑定,另一个nettcp绑定 到目前为止,我们没有创建任何的服务端点,因此ServiceHost将为两个地址创建默认的端点 1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using System.ServiceModel; 6: using System.ServiceModel.Description; 7: using WcfService1; 8: namespace ConsoleApplication1 9: { 10: class Program 11: { 12: static void Main(string[] args) 13: { 14: ServiceHost host = new ServiceHost(typeof(WcfService1.Service1), 15: new Uri("http://localhost:8080/service"), 16: new Uri("net.tcp://localhost:8081/service")); 17: host.Open(); 18: foreach (ServiceEndpoint se in host.Description.Endpoints) 19: { 20: Console.WriteLine("Address: " + se.Address.ToString() + 21: " Binding: " + se.Binding.Name + 22: " Contract :"+ se.Contract.Name); 23: } 24: Console.ReadKey(true); 25: } 26: } 27: }
默认端点的默认协议如下:
如果我们添加了端点,那么WCF将不支持默认端点
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using System.ServiceModel; 6: using System.ServiceModel.Description; 7: using WcfService1; 8: namespace ConsoleApplication1 9: { 10: class Program 11: { 12: static void Main(string[] args) 13: { 14: ServiceHost host = new ServiceHost(typeof(WcfService1.Service1), 15: new Uri("http://localhost:8080/service"), 16: new Uri("net.tcp://localhost:8081/service")); 17: host.AddServiceEndpoint(typeof(IService1), 18: new WSHttpBinding(), 19: "myBinding"); 20: host.Open(); 21: foreach (ServiceEndpoint se in host.Description.Endpoints) 22: { 23: Console.WriteLine("Address: " + se.Address.ToString() + 24: " Binding: " + se.Binding.Name + 25: " Contract :"+ se.Contract.Name); 26: } 27: Console.ReadKey(true); 28: } 29: } 30: }WCF映射协议绑定如下:
所以,如果我们没有配置任何的端点,WCF创建的默认端点将是basicHttpBinding类型net.tcp类型将被映射为netTcpBinding 如我们想改变这个,我们可以改变默认映射:
1: <<>protocolMapping> 2: <<>add scheme="http" binding="wsHttpBinding" bindingConfiguration="" /> 3: protocolMapping>随着上面的改变,如果我们不创建任何端点,WCF将创建默认端点,绑定wsHttpBinding,去演示这个,我们创建一个WCF服务应用程序,然后创建服务契约:
1: [ServiceContract] 2: public interface IService1 3: { 4: [OperationContract] 5: string GetData(); 6: }实现服务:
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Runtime.Serialization; 5: using System.ServiceModel; 6: using System.ServiceModel.Web; 7: using System.Text; 8: namespace WcfService1 9: { 10: public class Service1 :IService1 11: { 12: public string GetData( ) 13: { 14: return "Hello From Iservice1 "; 15: 16: } 17: } 18: }现在我们添加控制台程序,引用服务,在控制台程序中添加app.config:
1: "1.0" encoding="utf-8" ?> 2: 3: 4: 5: "http" binding ="wsHttpBinding" bindingConfiguration ="" /> 6: 7: 8:下面我们去取默认端点:
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using System.ServiceModel; 6: using System.ServiceModel.Description; 7: using WcfService1; 8: namespace ConsoleApplication1 9: { 10: class Program 11: { 12: static void Main(string[] args) 13: { 14: ServiceHost host = new ServiceHost(typeof(WcfService1.Service1), 15: new Uri("net.tcp://localhost:8081/service")); 16: host.Open(); 17: foreach (ServiceEndpoint se in host.Description.Endpoints) 18: { 19: Console.WriteLine("Address: " + se.Address.ToString() + 20: " Binding: " + se.Binding.Name + 21: " Contract :"+ se.Contract.Name); 22: } 23: Console.ReadKey(true); 24: } 25: } 26: }输出如下:
从上面我们可以看出我们已经修改了默认映射
七.开启WCF REST服务的帮助页面
首先我们创建一个项目,选择控制台程序作为项目类型 添加一个类库项目到解决方案 在两个项目中添加如下的引用:
System.ServiceModel System.ServiceModel.Description System.ServiceModel.Web下一步我们创建服务契约: 在类库项目中删除Class1.cs,添加一个接口文件,标记接口为public,并添加服务契约属性,定义两个操作契约,增加一个WebGet属性到第一个,添加WebInvoke属性到第二个 IService.cs
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using System.ServiceModel; 6: using System.ServiceModel.Web; 7: namespace Contracts 8: { 9: [ServiceContract] 10: public interface IService 11: 12: { 13: [OperationContract] 14: [WebGet] 15: string GetMessage(string inputMessage); 16: [OperationContract] 17: [WebInvoke] 18: string PostMessage(string inputMessage); 19: 20: } 21: }实现契约,在控制台下添加类文件: Service.cs
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using Contracts; 6: namespace SelfHostedRESTService 7: { 8: public class Service :IService 9: { 10: public string GetMessage(string inputMessage) 11: { 12: return "Calling Get for you " + inputMessage; 13: 14: } 15: public string PostMessage(string inputMessage) 16: { 17: return "Calling Post for you " + inputMessage; 18: } 19: } 20: } 现在我们在控制台程序下托管服务,打开Program.cs,创建WebServieceHostFactory的一个实例: 1: WebServiceHost host = new WebServiceHost(typeof(Service), new Uri("http://localhost:800)); 添加一个服务端点: 1: ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "");随着帮助页的开启,添加一个托管服务行为:
1: host.Description.Endpoints[0].Behaviors.Add(new WebHttpBehavior { HelpEnabled = true });众所周知,REST服务用webHttpBindding,我们在这里开启帮助页:
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: using System.ServiceModel; 6: using System.ServiceModel.Description; 7: using System.ServiceModel.Web; 8: using Contracts; 9: namespace SelfHostedRESTService 10: { 11: class Program 12: 13: { 14: static void Main(string[] args) 15: 16: { 17: WebServiceHost host = new WebServiceHost(typeof(Service), new Uri("http://localhost:8000")); 18: ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), ""); 19: host.Description.Endpoints[0].Behaviors.Add(new WebHttpBehavior { HelpEnabled = true }); 20: host.Open(); 21: Console.WriteLine("Service is up and running"); 22: Console.WriteLine("Press enter to quit "); 23: Console.ReadLine(); 24: host.Close(); 25: 26: } 27: } 28: }按下F5,运行服务,输出效果如下: 如果服务运行正常,我们通过输入http://localhost:8000/help测试帮助页
当你点击GET或者POST链接,你将得到如下的帮助信息:
下面的文章我们将探索其他的WCF4.0特性