SOA架构开发
SOA介绍
1、什么是SOA
SOA是:面向服务的体系结构(Service Oriented Architecture)指的是一个系统的架构,他的研究领域是大型分布式系统的“架构”范式。而OOP指的是程序具体开发过程中的“编程”范式。再通俗一点说就是在一个具体的软件内我们谈论OOP,但是在讨论软件与软件,系统与系统的关系的时候我们会借助SOA的理念帮助我们处理他们(指各个系统)之间的关系。
2、SOA的核心理念
支持异质(异构)
不要奢望企业中的每个系统都是java平台的,不要奢望企业中的每个系统都运行在Window平台上,不要奢望企业中的数据库都是结构化的……随着时间的流逝老版本的系统依在(里面的数据是无价的,别跟我说可以重新开发一套新系统,然后把旧系统的数据导出来,这就相当于让一个环境的某个物种短时间内重新进化,梦一样幼稚的想法!)新系统层出不穷,SOA要做的就是在这些异质(异构)的系统之上保证他们的“和平相处”。
支持变化
SOA绝不是一次性的事件,随着企业的发展要在系统环境的变化中产生出具有自己风格的SOA,这次整合了新旧系统兴许不就的将来,一次公司的收购就会迎来新的一轮SOA过程。
松耦合
目的是保证整个系统的灵活性和可伸缩性。
并且:
SOA中,服务之间应保持独立。更改一个服务不能影响到服务的调用。
SOA由一些通过标准消息通信的服务组成,标准消息机制让他们彼此独立。服务之间应该能进行异步通信。服务应该提供安全的通信和可靠的消息服务。
3、企业服务总线ESB
企业服务总线(Enterprise Service Bus)是SOA的基础设施,用于保证消费者能够调用供应者提供的服务。
ESB的核心作用
数据格式转换
因为SOA是支持异质的,所以ESB主要的作用就是处理不同平台不同语言之间的相互调用。那么首先要做的就是数据格式的转换。
引入一个中间格式然后将不同平台中的API都映射到这种格式上来。大多数情况人们选择的是和平台无关的WebServices技术,于是SOAP也就成为了这种中间格式。
(智能)路由
不同优先级的消息,不同类型的消息是需要给予不同处理。从这个角度说ESB就不单单是一个传递消息的通道了,还需要有一套自己的能识别其他服务提供数据方法。关于智能路由的另一个作用就是负载均衡。
ESB的各个职责都是“提供互操作性”这个核心任务的扩展。很明显异质系统通过ESB“连接”到了一起,并且支持“互操作”那么原有系统的安全是一个很大的问题。还需要注意的是不同协议提供不同形式的可靠性,例如WebServices中的HTTP协议无法保证消息传递的成功(周所周知HTTP协议是无连接、无状态的)。随着SOA的范围增大,连接到ESB上的系统增多时高效的管理服务将成为ESB最棘手的问题。
Web服务(Web Service)
1、什么是Web Service
Web Service就是基于Web的服务。它使用Web(HTTP)方式,接收和响应外部系统的某种请求。从而实现远程调用。
它由SOAP--WebService之间的基本通信协议、WSDL--WebService描述语言两部分组成。
Web Service的设计是为了解决不同平台,不同语言的技术层的差异。使用Web Service无论使用何种平台,何种语言都能够使用Web Service提供的接口,各种不同平台的应用程序也可以通过Web Service进行信息交互。
Web Service还具有以下特性:
实现了松耦合
应用程序与Web Service执行交互前,应用程序与Web Service之间的连接是断开的,当应用程序需要调用Web Service的方法时,应用程序与Web Service之间建立了连接,当应用程序实现了相应的功能后,应用程序与Web Service之间的连接断开。应用程序与Web应用之间的连接是动态建立的,实现了系统的松耦合。
跨平台性
Web Service是基于XML格式并切基于通用的Web协议而存在的,对于不同的平台,只要能够支持编写和解释XML格式文件就能够实现不同平台之间应用程序的相互通信。
语言无关性:无论是用何种语言实现Web Service,因为Web Service基于XML格式,只要该语言最后对于对象的表现形式和描述是基于XML的,不同的语言之间也可以共享信息。
描述性
Web Service使用WSDL作为自身的描述语言,WSDL具有解释服务的功能,WSDL还能够帮助其他应用程序访问Web Service。
可发现性
应用程序可以通过Web Service提供的注册中心查找和定位所需的Web Service。
Web Service也是使用和制作分布式所需的条件,使用Web Service能够让不同的应用程序之间进行交互操作,这样极大的简化了开发人员的平台的移植难度。
2、Web Service体系结构
Web Service由服务提供者和服务消费者组成。
服务提供者也可以称为服务的拥有者,它通过提供服务接口使Web Service在网络上是可用的。服务接口是可以被其他应用程序访问和使用的软件组件。
服务请求者也称为Web Service的使用者,服务请求者可以与服务提供者进行通信。相对于Web Service而言,服务请求者是寻找和调用提供者提供的接口的应用程序。
3、Web服务协议栈
在Web Service体系结构中,为了保证体系结构中的每个角色都能够正确和执行Web Service体系结构中的发布、查找和绑定操作,Web Service体系必须为每一层标准技术提供Web Service协议栈。
在Web Service协议栈中,最为底层的是网络传输层,Web服务协议是Web Service协议栈的基础。用户需要通过Web服务协议来调用服务接口。网络传输层可以使用多种协议,包括HTTP、FTP以及SMTP。
在网络传输层上一层的则是消息传递层,消息传递层使用SOAP(简单对象访问协议)作为消息传递协议,以实现服务提供者,服务注册中心和服务请求者之间进行信息交换。
在消息传底层之上的是服务描述层,服务描述层使用WSDL(Web Services Description Language)作为消息协议,WSDL使用XML语言来描述网络服务,在前面的章节中也讲到,WSDL具有自我描述性,它能够提供Web服务的一些特定信息。服务描述层包括了WSDL文档,这些文档包括功能、接口、结构等定义和描述。
在服务描述层之上的是服务发布层,该层使用UDDI协议作为服务的发布/集成协议。UDDI提供了Web服务的注册库,用于存储Web服务的描述信息。服务发布层能够为提供者提供发布Web服务的功能,也能够为服务请求者提供查询,绑定的功能。
当Web Service中触发了事件,如服务提供者发布服务接口、服务请求者请求服务等,服务提供者首先使用WSDL描述自己的服务接口,通过使用UDDI在服务器发布层向服务注册中心发布服务接口。服务注册中心则会返回WSDL文档。当服务请求者对服务注册中心执行服务请求,请求着通过WSDL文档的描述绑定相应的服务接口。
4、简单Web Service示例
在了解了Web Service基本的概念和协议栈的运行过程后,我们开始一个例子。
发布服务
新建一个项目(ASP.NET或MVC)作为Host。
1、添加一个“Web Service”文件(这个文件的后缀名是asmx)文件主要代码如下:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{
[WebMethod] //声明为Web方法
public string HelloWorld()
{
return "Hello World";
}
}
2、修改、添加WebMethod方法
如:
[WebMethod]
public int Add(int a, int b)
{
return a + b;
}
[WebMethod]
public int Subtract(int a, int b)
{
return a - b;
}
3、运行。运行结果如下:
至此,一个简单的WebService就发布完成了。
浏览器中看到的URL(类似于:http://localhost:8079/CalculatorService.asmx)就是我们的服务地址。
调用服务(消费者)
创建一个项目(ASP.NET、MVC、控制台、WinForm都行)作为Client。
1、添加一个服务引用,服务地址就是Host发布的那个地址(如果Host和Client在同一个解决方案下,我们也可以使用Discover来寻找服务地址),并为这个服务定义一个命名空间(比如CalculatorServiceRef)。
2、创建对应的SoapClient实例,调用其中的方法
如:
var client = new CalculatorServiceRef1.CalculatorServiceSoapClient();
Console.WriteLine(client.Add(1, 2));
这样,对服务的调用就完成了。
5、有权限认证的Web Service
为了防止服务被随意调用,我们需要对调用者进行身份认证。
可以使用SoapHeader实现权限认证。
1、创建SoapHeader
public class MySoapHeader : SoapHeader
{
public bool IsValidate()
{
......
}
}
2、修改WebService,使其支持SoapHeader
public class AuthService : System.Web.Services.WebService
{
//声明Soap头实例
public MySoapHeader myHeader = new MySoapHeader();
[SoapHeader("myHeader")]
[WebMethod]
public string HelloWorld()
{
myHeader.IsValidate();
return "嗨,终于有安全感了!";
}
}
这样,有权限认证的Web Service就已完成。对这个服务的调用代码将会发生改变:
var client = new AuthServiceRef.AuthServiceSoapClient();
var header = new AuthServiceRef.MySoapHeader { UserName = "admin", Password = "pwd" };
Console.WriteLine(client.Add(header, 3, 5));
WCF
1、了解WCF
Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台。它整合了原有的windows通讯的 .net Remoting,WebService,Socket的机制,并融合有Http和Ftp的相关技术。是Windows平台上开发分布式应用最佳的实践方式。
WCF框架模型如下:
WCF 提供了创建安全的、可靠的、事务服务的统一框架,WCF 整合和统一了现有分布式系统的开发技术,如 Enterprise Services、Messaging、Remoting、Web Services、WSE 等技术。
WCF对现有技术的封装,使开发人员可以无需关心ASMX、.Net Remoting这些技术的实现细节。
WCF和其他分布式技术对比表
WCF遵循客户端/服务器模型在应用程序之间进行通信,客户端程序能够通过服务器端提供的EndPoint端直接访问服务,如下所示。
WCF中绝大部分的实现和功能都包含在一个单独的程序集System.ServiceModel.dll中,命名空间为System.ServiceModel。通过使用System.ServiceModel命名空间能够快速搭建WCF应用程序环境。
WCF中的服务可以是本地的,也可以使用远程的服务。对于客户端而言,客户端只需要通过使用服务来实现应用程序功能,这些客户端也可以是不同的类型,包括Windows应用程序,ASP.NET应用程序甚至是移动终端。
2、WCF的主要构成
Endpoint
Endpoint是WCF实现通信的核心要素。一个WCF Service由一个Endpoint集合组成,每个Endpoint就是用于通信的入口,客户端和服务端通过Endpoint交换信息。
Endpoint由三部分组成:Address,Binding,Contract。为便于记忆,我们将这三部分称为是WCF的ABC。
Address
Address通过一个URI唯一地标识一个Endpoint,并告诉潜在的调用者如何找到这个Endpoint。
Binding
Binding实现在Client和Service通信的所有底层细节。比如Client与Service之间传递的Message是如何编码的—— text/XML, binary,MTOM;这种Message的传递是采用的哪种Transport——TCP, Http, Named Pipe, MSMQ; 以及采用怎样的机制解决Secure Messaging的问题——SSL,Message Level Security。所以Binding解决的是How to communicate with service?
Contract
Contract的主要的作用是定义通信协议,具体Service提供了哪些方法。
Behavior
Behavior: Behavior的主要作用是定制Endpoint在运行时的一些必要的Behavior。比如服务元数据的发布设置,Service回调Client的Timeout;Client采用的Credential type;以及是否支持Transaction等。
WCF地址
在Internet中,为了标识每个计算机,就需要使用IP进行地址划分,同样对于WCF服务而言,每个WCF服务都有一个自己的地址。
WCF地址包含两个元素,服务位置与传输协议,服务位置包括目标机器名、站点或网络、通信端口、管道或队列,以及一个可选的特定路径或者URI。传输协议包括:
HTTP:超文本传输协议。
TCP:传输控制协议。
Peer network:对等网。
IPC:基于命名管道的内部进程通信协议。
MSMQ:微软消息队列。
地址通常通过[基地址]/[可选的URI]的格式进行WCF地址描述,示例地址如下所示。
http://localhost:8731
http://localhost:8731/18-2
net.tcp://localhost:8731/server/18-2
net.pipe://localhost/18-2
net.msmq://localhost/18-2
3、契约
在WCF中,所有的WCF服务都会被公开成为契约。契约是服务的功能的标准描述方式,通常情况下WCF包含四种类型的契约。
示例如下。
[ServiceContract] //标识服务契约
public interface IService1 //服务接口
{
[OperationContract] //要发布的方法
string GetData(int value); //定义方法
}
服务契约(Service Contract):服务契约定包括两种:ServiceContract和OperationContract
ServiceContract用于类或者结构上,用于指示WCF此类或者结构能够被远程调用,而OperationContract用于类中的方法(Method)上,用于指示WCF该方法可被远程调用。
数据契约(Data Contract):数据契约定义了客户端与服务器之间交互的数据类型。
错误契约(Fault Contract):错误契约定义了操作中出现的异常,包括定义服务出现的错误并传递返回给客户端。
消息契约(Message Contract):消息契约能自定义消息格式,包括消息头,消息体,还能指示是否对消息内容进行加密和签名。
WCF服务如果需要复杂类型做参数,就必须对这个类型序列化。如果这个类型没有标注为DataContract和DataMember的话,相当于标注了DataContract,并且默认所有public的属性都要序列化。但是加的话,只有标注的属性(或字段)才能序列化,而且即使那个属性是私有的,一样可以序列化,不受任何限制。
MessageContract和DataContract的区别:
使用MessageContract时,属性要标注为MessageHeader或MessageBodyMember
使用DataContract时,属性要标注为DataMember
如果Service和Client使用相同的契约(同为DataContract或MessageContract),那么客户端的使用是比较简单的,否则有点麻烦。具体见:http://www.cnblogs.com/Jax/archive/2010/05/08/1730807.html
4、WCF中支持的传输协议
包括HTTP、TCP、Peer network(对等网)、IPC(基于命名管道的内部进程通信)以及MSMQ(微软消息队列),每个协议对应一个地址类型:
1.TCP地址
TCP地址使用TCP传输控制协议作为通信协议,如:net.tcp://localhost:8731/server/18-2
如果端口号没有指定,则TCP会使用默认端口号808作为其默认端口,如:net.tcp://localhost/server/18-2
2.HTTP地址
HTTP地址使用HTTP传输控制协议作为其通信协议,如:http://localhost:8731/18-2
如果端口号没有指定,则HTTP会使用默认的端口号80作为其默认端口。
3.IPC和MSMQ地址
IPC地址使用net.pipe作为通信协议,如:net.pipe://localhost/18-2
(适用于跨进程,不能使用于不同机器间)
因为IPC地址使用net.pipe进行传输,所以IPC地址将使用Windows的命名管道机制。在WCF中,如果服务使用命名管道,则该服务只能接收来自同一台客户端计算机的调用。因此,在使用时必须明确的指定WCF提供服务的计算机名,从而为管道名提供一个惟一的标识字符串。
而MSMQ地址使用net.msmq进行传输,即:使用了微软消息队列机制,如:net.msmq://localhost/18-2
5、WCF中提供的绑定有:
BasicHttpBinding: 最简单的绑定类型,通常用于 Web Services。使用 HTTP 协议,Text/XML 编码方式。
WSHttpBinding: 比 BasicHttpBinding 更加安全,通常用于 non-duplex 服务通讯。
WSDualHttpBinding: 和 WSHttpBinding 相比,它支持 duplex 类型的服务。
WSFederationHttpBinding: 支持 WS-Federation 安全通讯协议。
NetTcpBinding: 效率最高,安全的跨机器通讯方式。
NetNamedPipeBinding: 安全、可靠、高效的单机服务通讯方式。
NetMsmqBinding: 使用消息队列在不同机器间进行通讯。
NetPeerTcpBinding: 使用 P2P 协议在多机器间通讯。
MsmqIntegrationBinding: 使用现有的消息队列系统进行跨机器通讯。如 MSMQ。
6、WCF的宿主
WCF无法无法独立运行,它必须寄宿在宿主程序上,WCF的宿主程序可以是Windows 服务、COM+应用程序、WAS(Windows Activation Services,Windows进程激活服务)或IIS、Windows应用程序,或简单的控制台应用程序及任何.net程序。
IIS
1、使用WCF服务应用程序。
使用VS的“WCF服务应用程序”模板,VS自动帮我们生成了一个IService服务解决,并且通过Service.svc实现了它,配置文件也已写好。
我们只需要修改IService以及 Service.svc就可以搞定WCF程序的开发。
并且,我们可以通过“WCF Test Client”对服务进行测试。方法是:鼠标点击.svc文件,然后F5。
2、使用ASP.NET
在项目里新建一个“WCF Service”文件。剩下的事情与1类似。
3、使用ASP.NET MVC
与2类似。
控制台
在项目里新建一个“WCF Service”文件。VS自动帮我们生成了一个IService服务解决,并且通过Service.svc实现了它,配置文件也已写好。
我们只需要修改IService以及 Service.svc。
然后在main函数中启动服务,方法是:
var host = new ServiceHost(typeof(Service1));
host.Open();//打开宿主
也可以不使用“WCF Service”文件,自己手动创建一个接口文件及其实现类。这样配置文件里面没有任何关于WCF的配置,我们直接在main函数配置并且启动服务。方法是:
Uri baseAddress = new Uri("http://localhost:12306/exam04.consolehost");
using (ServiceHost host = new ServiceHost(typeof(Exam04), baseAddress))
{
host.AddServiceEndpoint(typeof(IExam04), new BasicHttpBinding(), "");
//ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
//smb.HttpGetEnabled = true;
//host.Description.Behaviors.Add(smb);
host.Open();
Console.WriteLine("WCF中的HTTP监听已启动....");
Console.ReadLine();
host.Close();
}
Windows应用程序
和控制台类似。
当然了,我们可以通过一个事件来启动WCF服务。
WAS
IIS7允许通过HTTP以外的协议进行激活和网络通信。此环境适合开发可通过WCF支持的任何网络协议(包括http、net.tcp、net.pipe、net.msmq)进行通信的WCF服务。部署简单、管理方便,这些网络协议在部署时可像Http一样,直接丢到IIS7上即可。
的WCF Service由一个Endpoint集合组成,每个Endpoint就是用于通信的入口,客户端和服务端通过Endpo
1、安装IIS的WCF激活组件
安装WCF的HTTP激活和非HTTP激活。
2、对网站添加net.tcp绑定
在网站的绑定里添加net.tcp,绑定信息中填写“808.*”。
3、对网站(或虚拟目录)开通net.tcp协议
网站(或虚拟目录)的高级设置里,在“已启用的协议”位置添加net.tcp,以逗号分隔。
4、建立服务程序
这一步和其它WCF服务的创建没有不同。只是,我们要修改配置文件的协议:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTctBinding">
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="Example4.WASHost.Service1" behaviorConfiguration="myTcpBehavior">
<endpoint address="" binding ="netTcpBinding" contract="Example4.WASHost.IService1" bindingConfiguration="netTctBinding"/>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost/Example4.WASHost/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="myTcpBehavior">
<serviceMetadata httpGetEnabled="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
注意:设置了tcp协议后,在IIS Express里浏览服务,肯定会报:找不到具有绑定 NetTcpBinding 的终结点的与方案 net.tcp 匹配的基址。注册的基址方案是 [http]。因为IIS Express没有添加net.tcp绑定,也没有开通net.tcp协议。
mex终结点绑定mexTcpBinding。并且behavior必须设置serviceMetadata,否则会显示:在服务 Service1 实现的协定列表中找不到协定名称 "IMetadataExchange"。而且需要将httpGetEnabled设置为false,否则只能打开http协议。
7、WCF服务的调用
在客户端程序上添加服务引用,输入服务的Address(如果宿主程序和客户端在同一解决方案下,可以使用discover功能寻找服务)。
创建服务对应的Client类。如:var client = new ServiceReference1.StudentServiceClient();
调用。如:var list = client.GetAll();
8、WCF服务的通信模式
WCF在通信过程中有三种模式:请求应答、单向、双工通信。
请求应答模式
之前的所有例子都是请求应答模式。
请求与答复模式为WCF的默认模式,即使返回值是void 也属于请求与答复模式。
缺点:如果用服务的处理时间很长,客户端要等待服务器处理完毕后才能继续执行,因此响应能力将会大大下降。
优点:客户端可以直接使用服务的返回值。
单工
客户端向服务端发送求,但是不管服务端是否执行完成就接着执行下面的程序。
单向模式要在OpertaionContract的属性中显示设置值,代码如下:
[OperationContract(IsOneWay = true)]
双工 callback 服务端定义客户端 创建上下端
双工模式建立在上面两种模式的基础之上,实现客户端与服务端相互的调用。相互调用:以往我们只是在客户端调用服务端,然后服务端有返回值返回客户端,而相互调用不光是客户端调用服务端,而且服务端也可以调用客户端的方法。
支持回调的绑定有4种:WSDualHttpBinding、NetTcpBinding、NetNamedPipeBinding、NetPeerTcpBinding。
因此服务端的binding要改为wsDualHttpBinding。
服务端:
[ServiceContract(CallbackContract = typeof(IService1Callback))]
public interface IService1
{
[OperationContract]
string ShowName(string name);
}
public interface IService1Callback
{
[OperationContract(IsOneWay = true)]
void PrintSomething(string str);
}
public class Service1 : IService1
{
IService1Callback callback = null;
public Service1()
{
callback=OperationContext.Current.GetCallbackChannel<IService1Callback>();
}
public string ShowName(string name)
{
......
callback.PrintSomething(“我完活了”);
return "搞定了" ;
}
}
客户端:
首先要有一个callback类
public class CallbackHandler : ServiceReference1.IService1Callback
{
public void PrintSomething(string str)
{
Console.WriteLine(str);
}
}
调用服务
InstanceContext instanceContext = new InstanceContext(new CallbackHandler());
var client = new ServiceReference1.Service1Client(instanceContext);
Console.WriteLine(client.ShowName("ddd"));
9、Security
最简单的就是通过MessageContract传递账号密码
10、不用svc文件
在config的serviceHostingEnvironment/serviceActivations下增加配置:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" >
<serviceActivations>
<add factory="System.ServiceModel.Activation.ServiceHostFactory"
relativeAddress="./RequestResponse4/Service1.svc"
service="Host4.Service1" />
</serviceActivations>
</serviceHostingEnvironment>
也可以简化为:
<add relativeAddress="RequestResponse4/Service1.svc" service="Host4.Service1" />
11、自定义地址路由映射(不用svc文件)
先在Global.asax中的Application_Start事件中添加以下代码:
void Application_Start(object sender, EventArgs e)
{
System.Web.Routing.RouteTable.Routes.Add(
new System.ServiceModel.Activation.ServiceRoute("MyService",
new System.ServiceModel.Activation.WebServiceHostFactory(),
typeof(Service)));
}
其中"MyService"为自定义要在地址栏中映射的名称
typeof(Service)中的"Service"为WCF中服务实现类Service.cs,一般继承IService.cs接口
接下来修改web.config
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true">
然后再在Service.cs服务类上面添加:
[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)] //记得引用using System.ServiceModel.Activation;
此外还有IService.cs接口的方法声明中设置UriTemplate以接受参数:
[OperationContract, WebGet(UriTemplate = "{Name}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml)]
string GetName(string Name);
(webconfig不能配system.serviceModel的services)好像是变成REST了?
配置文件
服务端的配置文件主要是对services、bindings、behaviors的配置。
<services>配置节
<services>配置节中可以定义多个服务,每一个服务都被放到<service>配置节中,WCF的宿主程序可以通过配置文件找到这些定义的服务并发布这些服务。
<service>配置节包含name和behaviorConfiguration属性。其中,name配置了实现Service Contract的类型名。类型名必须是完整地包含了命名空间和类型名。而behaviorConfiguration的配置值则与其后的<behaviors>配置节的内容有关。
<endpoint>是<service>配置节的主体,其中包含了endpoint的三个组成部分:A、B和C。
如果address值为空,那么endpoint的地址就是默认的基地址(Base Address)。这里所谓的基地址可以通过配置<host>来定义。
<identity> 节点下面,Certificate用于指定 X.509 证书的设置。此元素的类型为 CertificateElement。它包含一个 encodedValue 属性,该属性是一个字符串,用于指定此证书编码的值。
dns 指定用于对服务进行身份验证的 X.509 证书的 DNS。此元素包含一个字符串属性 value,并包含实际的标识。
<behaviors>配置节
如果需要指定服务在执行方面的相关特性时,就必须定义服务的behavior。
例如通过指定ServiceMetadataBehavior,可以使WCF服务对外公布Metadata。
在WCF中,behavior被定义为Attribute,其中,ServiceBehavior和OperationBehavior是最常用的behavior。通过他们可以控制服务的如下属性:
1、 对象实例的生命周期;
2、 并发与异步处理;
3、 配置行为;
4、 事务行为;
5、 序列化行为;
6、 元数据转换;
7、 会话的生命周期;
8、 地址过滤以及消息头的处理;
9、 模拟(Impersonation)。
也可以通过代码的方式指定地址。如:
using (ServiceHost serviceHost = new ServiceHost(typeof (Service1)))
{
serviceHost.AddServiceEndpoint(typeof (IService1), new WSHttpBinding(),
"http://127.0.0.1:9999/Service1");
serviceHost.Open();
}
还可以通过“基地址+相对地址”的方式进行设置终结点地址。对于一个服务来说,可以指定一个或多个基地址,但是对于一种传输方式协议类型,只能具有一个唯一的基地址。
这也可以通过代码实现:
Uri[] baseAddress =
{
new Uri("http://127.0.0.1:8888/myservices"),
new Uri("net.tcp://127.0.0.1/:8888")
};
using (ServiceHost serviceHost = new ServiceHost(typeof (Service1),baseAddress))
{
serviceHost.AddServiceEndpoint(
typeof (IService1),
new BasicHttpBinding(),
"Service1");
serviceHost.AddServiceEndpoint(
typeof (IService1),
new NetTcpBinding(),
"Service1");
serviceHost.Open();
}
<bindings>配置节
定义一个或多个系统提供的binding元素,例如<basicHttpBinding>
RBAC
权限管理包括两部分工作:权限的设计,权限控制点。
A、权限的设计
通常采用RBAC(基于角色的访问控制)方案。
通常会涉及:用户表、权限表、角色表、用户角色关系表、角色权限关系表5张表。有时还会涉及菜单表、页面表、菜单权限关系表、菜单页面关系表 等。
B、权限控制点
权限控制点是指:怎么防止非法访问?如果只是基于菜单的权限控制,那么用户可以直接通过URL地址访问自己不该访问的页面。因此,需要一个全局的控制点,以保证所有的请求都会先经过控制点,然后进行身份验证已决定这个访问是否放行。
在ASP.NET里,我们可以将HttpHandler、HttpModule作为权限控制点。也可以通过重写Page类实现权限控制。MVC里可以通过过滤器进行控制。
常见错误或技巧
WCF无法引入Model实体
客户端无法调出service里需要的实体。这时候只需要在重新引用服务或者更新引用服务的时候,点“高级”按钮,在服务引用设置对话框中,将“重新使用引用的程序集中的类型”这个勾取消,这样就能安全的重新引用服务。
IncludeExceptionDetailInFaults
获取或设置一个值,该值指示是否在错误中包含有关异常的详细信息
httpGetEnabled
httpGetEnabled="true"表示允许用http的get方式获取服务的元数据信息。如果用浏览器访问时,得到一个“当前已禁用此服务的元数据发布”的提示,就是因为不允许以http的get方式获取服务元数据造成的,这个属性就是开启此功能。
顺便提一下,如果要生成客户端代理的话,对http类型的binding,必须要开放get方式访问元数据。
mex这个endpoint
这个endpoint比较特殊,它的binding是mexHttpBinding,服务契约是IMetadataExchange。这个endpoint是用于元数据发布的,它的功能实际上和刚才的httpGetEnabled="true"有些重复。
我们可以这样理解,当开启了httpGetEnabled时,用 http://...../DataService.svc?wsdl 就可以访问到元数据;如果没开启,但有这个endpoint,用 http://...../DataService.svc/mex 也可以访问到元数据;如果都没有,那对不起,不允许你获取元数据。(当然啦,如果你已经有契约了,不会影响调用的)
多加一句,对tcp类型的binding,有一个对应的mexTcpBinding用于获取元数据,没有定义它,svcutil.exe就不能生成tcp类binding的代理类。
没有发布mex的服务,不能“go”,可以被discover但无法引用。但如果以前已经引用过,就可以继续使用。
basicHttpBinding和wsHttpBinding
basicHttpBinding默认使用SOAP1.1.。
wsHttpBinding默认使用SOAP1.2(确切应该是Soap12WSAddressing10)
SOAP1.1有SOAPAction项,SOAP1.2没有,当然SOAP1.2和1.1还有其他的区别,具有请查看w3的文档
basicHttpBinding默认是使用soap1.1,但是basicHttpBinding是明文传输的,wsHttpBinding可以实现加密传输,但soap是soap1.2;可以利用自定义绑定(customBinding)设置soap版本为soap1.1,并且使用加密等传输方式:
runAllManagedModulesForAllRequests
在Global.ascx中,如想对每个Request都执行Application_BeginRequest,那么必须要在system.web或system.webServer中的modules中加入此属性:
<modules runAllManagedModulesForAllRequests="true">
AspNetCompatibilityRequirementsAttribute
AspNetCompatibilityRequirementsAttribute 类用于 Windows Communication Foundation (WCF) 服务以指示该服务能否在 ASP.NET 兼容模式下运行。
在应用于服务实现类时,此属性可指示该服务是否需要 ASP.NET 兼容模式,或是否支持为宿主应用程序域 (AppDomain) 启用该兼容模式。
AppDomains 宿主 WCF 服务可在两种不同的宿主模式下运行:
混合传输模式(默认):在这种模式下,WCF 服务不参与 ASP.NET HTTP 管线。 这可保证 WCF 服务行为的一致性,使其不受宿主环境和传输的影响。
ASP.NET 兼容模式:在这种模式下,WCF 服务以与 ASMX 服务类似的方式参与 ASP.NET HTTP 管线。ASP.NET 功能(例如,文件授权、URL 授权和 HTTP 会话状态)适用于在此模式下运行的 WCF 服务。
宿主模式由应用程序级配置标志 aspNetCompatibilityEnabled 控制。
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
默认情况下,此标记为 false,因此,除非明确选择使用 ASP.NET 兼容模式,否则 WCF 服务将在混合传输模式下运行。
有关以下内容的详细信息ASP.NET 兼容模式,请参见 <serviceHostingEnvironment>。
使用 RequirementsMode 属性可以实现此目的。 运行时,应用程序可以通过检查静态属性AspNetCompatibilityEnabled 的值,来检测是否启用了 ASP.NET 兼容模式。
IIS 可以为每个站点指定多个绑定,这会导致每个方案有多个基址。 在 .NET Framework 3.5 之前,WCF 不支持一个方案有多个地址;如果指定了多个地址,则在激活期间引发 ArgumentException。
通过 .NET Framework 3.5,Internet 服务提供商可以在同一站点上使用同一方案的不同基址承载多个应用程序。
例如,一个站点可能包含以下基址:
http://payroll.myorg.com/Service.svc
http://shipping.myorg.com/Service.svc
从 .NET 4 开始,通过将 ServiceHostingEnvironment 的 MultipleSiteBindingsEnabled 设置为 True,您无需选取单个基址便可实现对 IIS 中多个绑定的支持。 此支持限于 HTTP 协议方案。
下面的配置代码示例在 <serviceHostingEnvironment> 上使用 multipleSiteBindingsEnabled。
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled=”true” >
</serviceHostingEnvironment>
</system.serviceModel>
对svc文件改名
对svc文件改名后,如果不同时修改svc文件的Markup,
就会报:“找不到类型“XXX”,它在 ServiceHost 指令中提供为 Service 特性值,或在配置元素 system.serviceModel/serviceHostingEnvironment/serviceActivations 中提供。”
启动WCF测试客户端
当按F5的时候,焦点选定在特定服务的.svc或.xamlx文件时,就会启动WCF测试客户端。
元数据发布的2种方式:httpGetEnabled与mex
1、元数据即WSDL,描述了服务的细节,以便客户端使用。
2、必须为服务配置ServiceMetadata行为,才能为其生成WSDL,才能再使用httpGetEnabled或mex将其公布出去
3、这两种方式公布出去的WSDL无区别。但公布的方式有区别
此方式通过在服务在的URL后加“?wsdl”的方式公布WSDL,可直接通过HTTP访问得到。
Behaviors节点
behaviors此元素定义名为 endpointBehaviors 和 serviceBehaviors 的两个子集合。 每个集合分别定义终结点和服务所使用的行为元素。 每个行为元素由其唯一的 name 特性标识。 从 .NET Framework 4 开始,不要求绑定和行为具有名称。 有关默认配置以及无名称绑定和行为的更多信息,请参见简化配置和 WCF 服务的简化配置。
<!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点--> <serviceMetadata httpGetEnabled="true"/>
默认情况,WCF会把服务抛出的异常以FaultException类型传递到客户端。使用FaultException<T>可以抛出更多细节,对T的唯一要求是必须标记为可序列化或数据契约。
服务里:throw new FaultException<MyFault>(new MyFault())
而OperationContract上加:[FaultContract(typeof(MyFault))]
baseAddresses
<endpoint address="ssss" binding="wsDualHttpBinding" contract="Host.IService1" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/Host/Service1/" />
</baseAddresses>
</host>
baseAddresses即基址。将来发布的服务地址是baseAddresses,mex地址是baseAddress/mex。
即:http://localhost:8733/Host/Service1/
但客户端配置文件中的服务地址是服务端的baseAddresses+endpoint的address。
即:http://localhost:8733/Host/Service1/ssss
Mex终结点的address设置必须设置为相对地址
即:必须有baseAddresses,否则抱:ServiceMetadataBehavior的HttpGetEnabled属性设置为true,而HttpGetUrl属性是相对地址,但没有http基址。
同时启动多个WCF服务
Var conf = ConfigurationManager.OpenExeConfiguration(Assembly.GetCallingAssembly().Location);
Var svcmode = (ServiceModelSectionGroup)conf.GetSectionGroup("system.serviceModel");
ServiceHost host = null;
foreach (ServiceElement el in svcmode.Services.Services)
{
string serviceNameSpace = el.Name.Split(‘.‘)[0];
Type svcType = Type.GetType(el.Name + "," + serviceNameSpace);
if (svcType == null)
throw new Exception("Invalid Service Type " + el.Name + " in configuration file.");
host = new ServiceHost(svcType);
host.Opened += delegate
{ Console.WriteLine(el.Name + "服务已经启动了"); };
host.Open();
}
服务端获取客户端IP
public string ClientIpAndPort()
{
OperationContext context = OperationContext.Current;
MessageProperties properties = context.IncomingMessageProperties;
RemoteEndpointMessageProperty endpoint =
properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return endpoint.Address + "port" + endpoint.Port;
}
无法激活服务,因为它不支持 ASP.NET 兼容性
错误提示:无法激活服务,因为它不支持 ASP.NET 兼容性。已为此应用程序启用了 ASP.NET 兼容性。请在 web.config 中关闭 ASP.NET 兼容性模式或将 AspNetCompatibilityRequirements 属性添加到服务类型且同时将 RequirementsMode 设置为“Allowed”或“Required”。
默认情况下ASP.NET兼容性支持是关闭的,但很多时候需要打开Asp.Net的兼容性来利用Asp.Net的一些特性(使用session,上下文等),具体可参考http://msdn.microsoft.com/zh-cn/library/ms752234.aspx。
如果要打开兼容性,需要做两步:
一是在服务类加上如下标记:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class AppRuntimeStateSvc : IAppRuntimeStateSvc
{
//服务代码.
}
二是在web.config中的<system.serviceModel>段里加:
<serviceHostingEnvironment
multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
注意框架3.5以前都是默认就支持的,4.0以后默认就是没有打开兼容性支持的.
REST
1、什么是REST
REST,即Representational State Transfer的缩写。翻译为"表现层状态转化"。
1、资源(Resources)
REST的"表现层"其实指的是"资源"(Resources)的"表现层"。因此URI就是每个资源的地址或识别符。
2、表现层(Representation)
"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
3、状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综述
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
误区
RESTful架构有一些典型的设计误区。
最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。
举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:
POST /accounts/1/transfer/500/to/2
正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:
POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00
另一个设计误区,就是在URI中加入版本号:
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分
目前在三种主流的Web服务实现方案中,因为REST模式与复杂的SOAP和XML-RPC相比更加简洁,越来越多的web服务开始采用REST风格设计和实现。
需要注意的是,REST是设计风格而不是标准。REST通常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。
资源是由URI来指定。
对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。
通过操作资源的表现形式来操作资源。
资源的表现形式则是XML或者HTML,取决于读者是机器还是人,是消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。
下表列出了在实现 含状态传输的 Web 服务时HTTP请求方法的典型用途。
HTTP 请求方法在RESTful Web 服务中的典型应用 |
||||
资源 |
GET |
PUT |
POST |
DELETE |
一组资源的URI,比如http://example.com/resources/ |
列出 URI,以及该资源组中每个资源的详细信息(后者可选)。 |
使用给定的一组资源替换当前整组资源。 |
在本组资源中创建/追加一个新的资源。 该操作往往返回新资源的URL。 |
删除整组资源。 |
单个资源的URI,比如http://example.com/resources/142 |
获取 指定的资源的详细信息,格式可以自选一个合适的网络媒体类型(比如:XML、JSON等) |
替换/创建 指定的资源。并将其追加到相应的资源组中。 |
把指定的资源当做一个资源组,并在其下创建/追加一个新的元素,使其隶属于当前资源。 |
删除指定的元素。 |
使用WebInvoke和WebGet暴漏服务:
服务可以使用WebHttpBinding以及WebGet或者WebInvoke属性来暴露。这些属性每一个都确定HTTP动作、消息格式以及需要暴露给一个操作的消息体形式。
WebGet属性使用GET动词暴露操作。
[OperationContract]
[WebGet(UriTemplate ="/customer/{id}")]
public Customer GetCustomer(int id)
{
//Get customer from database
return customer;
}
[OperationContract]
[WebInvoke(Method="PUT", UriTemplate="/customer/{id}")]
public void PutCustomer(int id, Customer customer)
{
//Put customer in database
}
[OperationContract]
[WebInvoke(Method="Delete", UriTemplate="/customer/{id}")]
public void DeleteCustomer(int id)
{
//Put customer in database
}
URL和URI
区别就是URI定义资源,而URL不单定义这个资源,还定义了如何找到这个资源。
2、RESTful WCF
服务定义
WCF 中通过 WebGetAttribute、WebInvokeAttribute (GET/PUT/POST/DELETE)、UriTemplate 定义 REST 的服务的调用方式, 通过WebMessageFormat (Xml/Json) 定义消息传递的格式。
[WebGet(UriTemplate = "Tasks/Json", ResponseFormat = WebMessageFormat.Json)]
[WebInvoke(UriTemplate="Task/{title}", Method="GET", ResponseFormat=WebMessageFormat.Json)]
Svc发布
通过 WCF 4.0 里创建的 WCF Service Application 发布REST服务只需要在 svc 的 Markup 里加上:
<%@ Factory="System.ServiceModel.Activation.WebServiceHostFactory"%>
BTW: 不过这样,WCF的Metadata就不能访问到了,也就说不能访问到svc的wsdl了。
通过浏览器访问
OK,在浏览器中键入 http://localhost:2571/TaskService.svc/Tasks/Xml 就能得到结果:
通过c#代码调用
客户端的调用利用System.Net.WebClient:
var client = new WebClient();
this.txtResponse.Text = client.DownloadString(url);
通过js调用
<script type="text/javascript">
$(document).ready(function () {
$("#btnGet").click(function () {
var url = $("#txtUrl").val();
$.get(url, function (data) {
for (var i = 0; i < data.length; i++)
$("#divResponse").append("<li>" +
data[i].Title + " - " +
data[i].Detail + "</li>");
});
});
});
</script>
使用Put、Delete、Post
可以使用WebClient
using (var client = new WebClient())
{
client.UploadString(url, "DELETE", "");
}
或者WebRequest
var request = WebRequest.Create(url);
request.Method = "DELETE";
var response = (HttpWebResponse)request.GetResponse();
但是WebClient不支持PUT和DELETE!(所以就简单使用GET、POST吧)
因为REST 是基于HTTP的, 所以对于 REST 的客户端的开发者,无法像传统的 WebService或者其他的WCF服务通过引用wsdl,享受“奢侈”的代码生成,而使用强类型的本地代理调用服务。 开发者只能通过 Http Request 的组装, 但正因为这种直接的HttpRequest组装,而使得客户端真正是语言无关的。这里不得不提一下 Microsoft.Http.dll 和 Microsoft.Http.Extensions.dll,它们是微软提供的REST客户端包。可以更加方便地操作 HttpRequest/Response,
但是:
The WCF REST Starter Kit is no longer supported. For building RESTful services on .NET please use *ASP.NET Web API.
Basic Security
在SOAP协议的WCF中,可以通过SOAPHeader(MessageHeader)来实现用户名密码的传输。在REST WCF中,可以利用 HttpHeader。
服务器端验证客户端传来的Header:
var ctx = WebOperationContext.Current;
var auth = ctx.IncomingRequest.Headers["a"];
if (string.IsNullOrEmpty(auth) || auth != "b")
{
ctx.OutgoingResponse.StatusCode = HttpStatusCode.MethodNotAllowed;
throw new Exception("账号密码错误");
}
客户端发送Header:
var request = WebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "GET";
request.Headers.Add("a", "c");
var response = (HttpWebResponse)request.GetResponse();
全局控制Security
public class MySecureWebServiceHostFactory : WebServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var host = base.CreateServiceHost(serviceType, baseAddresses);
host.Authorization.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
return host;
}
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
var host = base.CreateServiceHost(constructorString, baseAddresses);
host.Authorization.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
return host;
}
}
public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
var ctx = WebOperationContext.Current;
var auth = ctx.IncomingRequest.Headers["a"];
if (string.IsNullOrEmpty(auth) || auth != "123")
{
ctx.OutgoingResponse.StatusCode = HttpStatusCode.MethodNotAllowed;
return false;
}
return true;
}
}
在Global中配置:
var securewebServiceHostFactory = new MySecureWebServiceHostFactory();
RouteTable.Routes.Add(new ServiceRoute("Task2Service",
securewebServiceHostFactory, typeof(Task2Service)));
3、直接在Web Application里发布RESTful WCF
服务定义
添加引用
System.Runtime.Serialization、
System.ServiceModel、
System.ServiceModel.Web、
System.ServiceModel.Activation
直接定义契约和其实现。
注册服务路由
在Globel.asax中的Application_Start事件中注册服务。并且注册的"XService"自动成为服务的基地址,即 http://<machine_name>:<port>/XService/
RouteTable.Routes.Add(new ServiceRoute("TaskService",
new WebServiceHostFactory(), typeof(TaskService)));
ASP.NET 路由集成功能要求 ASP.NET 兼容性
如果要与ASP.NET 并行承载 WCF 服务,并在 ASP.NET 兼容模式中承载它们。需要设置ASP.NET兼容性。
尽管 WCF 模型旨在跨宿主环境和传输保持行为的一致性,但经常在一些方案中,应用程序中不要求这种程度的灵活性。 WCF 的 ASP.NET 兼容模式适用于具有以下特点的方案:不需要具有在 IIS 外部承载或通过 HTTP 之外的协议进行通信的能力,但使用 ASP.NET Web 应用程序平台的所有功能。
在默认的并行配置中,承载基础结构的 WCF 截获 WCF 消息并将其路由到 HTTP 管道之外;与此不同的是,在 ASP.NET 兼容模式中运行的 WCF 服务完全参与 ASP.NET HTTP 请求生命周期。 在兼容模式中,WCF 服务通过 IHttpHandler 实现来使用 HTTP 管道,其方式类似于处理 ASPX 页和 ASMX Web 服务的请求。 因此,WCF 的行为在以下 ASP.NET 功能方面与 ASMX 相同:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Web API
1、了解WCF
Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台。它整合了原有的windows通讯的 .net Remoting,WebService,Socket的机制,并融合有Http和Ftp的相关技术。是Windows平台上开发分布式应用最佳的实践方式。
WCF框架模型如下: