利用System.ServiceModel连接Web Services

日期: 2008-03-19 来源:TechTarget中国

  新的命名空间System.ServiceModel是Windows Vista的一个核心构件,它将会该改变.NET开发者对构建服务和确保服务的安全的认识。在开发者可以开发完全成熟的安全技术保障的Web services以前,他们需要知道如何使得一个Web service能够同其他的Web service交流,并且Microsoft的新的Windows Communication Foundation能够完成这些。


  它需要的时间并不比一个迅速的偷看WCF的内部花费更多的时间,并且它设计出来的目标能够体现出它的体系结构的好处。构建一个成功的系统的关键是构建一个持久的系统。虽然这听起来很简单,这却是一个需要去完成的很高的任务,特别的在现在商业环境变得这么流行。让事情更糟的是,有过多的技术架构来提供给许多不同的场合。


  WCF设计成具有那些主要的目标,这些目标是通过统一所有的 Microsoft技术概念栈来解决这些问题中的一些,这些技术包括类似EnterpriseServices (COM+), System.Messaging (MSMQ), Interoperable Basic Web Services (ASMX) 和具有单独编程模型的高级WS-* stack (WSE)。WCF的整个架构都是围绕消息类型的概念的,消息类型只是一个抽象的电缆一级的SOAP消息,SOAP是一个特殊的XML模式。所有开发者知道的是他们使用WCF所创建的解决方案将会在电缆上产生SOAP消息,用以同在其他地方的使用其他技术和平台提供者实现的应用程序解决方案进行交互。


  架构:层和通道


  简单的来看它,WCF解决方案由两层构建而成。 底层涉及到消息基础架构,这一层关注它自己的消息的从一个端点到另外一个断定的移动。这一层是由System.ServiceModel命名空间中的类组成的。几个可扩展的地方可以定制这些类的行为。顺带提及的,第二层是编程模型,是构建在这些可扩展点之上的。它由System.ServiceModel中的另外一个类集合构成的,并且这些类是WCF开发者在构建WCF应用程序的时候主要涉及到的类。


  WCF编程模型是被设计来实现一些基础性的目标的。一个关键的目标是提供一个标准的接口来在服务之间提供交互连接网络的接口。换一句话说就,就是你不需要去重复“修改代码,编译,构建,测试和部署”的循环,即使有一个传输交换或者网络交换的需要,例如在HTTP和TCP之间的转换, 或者在一个网络的消息被加密了,而在另外一个却没有被加密。


  WCF的另外一个相关的关键设计目标是使构建在它上面的应用程序和服务可以持久。它通过隐藏可能会被证明成为现在的Web Services规范的同时代陈旧之物,这些在编程模式之后的规范提供了实时的,有用的商业功能。从而在开发商业细节和部署技术细节之间有一个清晰的抽象边界。通过不混淆商业逻辑代码和技术细节,解决方案看起来更加清晰和简练,并且具有更好的机会更加灵活,从而能在当今和明天的流行的面向服务的环境中生存下来。


  这些目标由一个分层的体系结构为主要的准则来实现的,这个架构本质上意味着WCF是一个可以互相扩展和被开发者和合作者替代的子框架的集合。


  从40,000-foot的角度来看,WCF由两个更大的框架构成:Typed层和通道层。


  通道层的数据的基本单元是消息。通道层是用来发送和接收消息的。而消息时一个非常灵活的对象,开发者需要具有相关信息和XML的知识来完全的利用它。从而,绝大部分的人很可能作的事情是依赖于Typed层作为他们的朋友和通过电缆来发送消息的接口。对应的Typed层的数据的基本单元是一个公共运行时语言类。


  在Typed层,创建实现服务或者Typed 代理的CLR对象。这一层将会转换参数并把它返回到消息对象和通道的方法调用中。通过这种方式,Typed层构建可扩展了在通道层之上的功能,而这些功能可以显然的调节任何的对通道的改变和(或)提高。这个体系结构对满足我上面提到的基本设计目标大有帮助。


  让我们看一些具体的代码来获得一个对这个设计的更好的感觉。


  看看契约


  WCF应用程序由服务和他们的用户(消费者)构成。服务定义了消费者进行市用来进行交互的终端。终端由一个地址,一个帮定和契约,也就是我们熟知的WCF的基础。


  契约是开发者主要关注的对象。开发者定义一个契约,实现它并使得能够提供服务的主机。


  WCF中有一些类型的契约能够以被分类为结构或者行为契约。让我们首先定义一个操作协议:


  <OperationContract()> _
  Function SubmitCreditCard(ByVal crdcard As CreditCard) As Boolean


  一个操作契约定义了一个操作,并且它是一个为服务运作的单位。它默认的指定一个消请求和一个恢复消息的交换模式(MEP).正如你在上面看到的示例代码,WCF的观念是把操作契约固定的映射到一个Web Services描述语言(WSDL)的方法定义,对应的一个WSDL的方法也固定的对应一个CLR方法/函数。


  下面,我们看一个简单的服务契约:


  <ServiceContract(Namespace:=”urn:demos.softwaremaker.net/…”)> _
Public Interface IMyFirstSecuredWCFService
 <OperationContract()> _
 Function SubmitCreditCard(ByVal crdcard As CreditCard) As Boolean
 End Interface


  ’Service class which implements the service contract.
  Public Class MyFirstSecuredWCFService : Implements   IMyFirstSecuredWCFService
  Public Function SubmitCreditCard(ByVal cc As CreditCard) As Boolean _
 Implements IMyFirstSecuredWCFService.SubmitCreditCard
  Try
  ‘Do some Credit Card Processing and return True if results are positive
  Catch
   Return False
  End Try
   Return True
 End Function
End Class


  这和代码优先的方式是不同的,在代码优先的方式中,可以直接使用WCF属性来修饰一个新的或者存在的类以及它的方法。我并不推荐这样,因为它并没有通过一个faÇade层来,而是固定的邦定一个接口契约和它的CLR中的实现类。


  请注意我上面描述的契约优先的途径和WSDL优先的途径有一些稍微的不同。在WSDL优先的方式中,你实际上是直接写一个WSDL文件,而不是让底层的平台自动的通过一个些属性编程和声明来生成一个WSDL文件。尽管我提倡WSDL优先的途径,在这种方式下,可以设定更多的通过WSDL契约和模式的控制,特别是在互操作场景下,当没有更好的工具,并且开发者只好在规范,特征文档和角撑架上挖掘相关的知识的时候,WCF的契约优先方式的定义的确是一个巨大的进步。


  下一步将是定义我的定制CreditCard类,这是一个简单的.NET CLR类型:


  <DataContract()> _
 Public Class CreditCard
 <DataMember(Name:=”AccountName”)> Private _AccountName As String
 <DataMember(Name:=”CreditCardType”)> Private _CreditCardType As String
 <DataMember(Name:=”CreditCardNo”)> Private _CreditCardNo As String
 <DataMember(Name:=”CreditCardExpiryDate”)> Private _CreditCardExpiryDate As String


 Public Property AccountName() As String
  Get
   Return _AccountName
  End Get
  Set(ByVal value As String)
   _AccountName = value
  End Set
 End Property


 Public Property CreditCardType() As String
  Get
   Return _CreditCardType
  End Get
  Set(ByVal value As String)
   _CreditCardType = value
  End Set
 End Property


 Public Property CreditCardNo() As String
  Get
   Return _CreditCardNo
  End Get
  Set(ByVal value As String)
   _CreditCardNo = value
  End Set
 End Property


 Public Property CreditCardExpiryDate() As String
  Get
   Return _CreditCardExpiryDate
  End Get
  Set(ByVal value As String)
   _CreditCardExpiryDate = value
  End Set
 End Property
End Class


  在WCF中的DataContract是一个结构契约类型,它是通过增加一个DataContract和DataMember属性到你的.NET类里面i。两个属性都是在System.Runtime.Serialization命名空间中定义的。


  数据契约声明所要做的就是具体详细说明一个.NET类型是如何在一个XML模式中表示的。从另外一个角度来看,两种不同的软件实体,可以具有不同的内部类的定义,尽管都是数据的相同的抽象表示。在这种情况下,两边都可以把数据的那些内部表示转化为相同的抽象表示的物理表现,或者从抽象表示的物理表现转化为那些内部表示。那些物理表现可以促进通讯和互操作性。XML模式是一个最长用到来组成用于在两个实体之间交换的数据的抽象表示,使用XML作为这种抽象表示的物理表现。


  一旦所有的都完成,就是制定集合的容器并把服务加入其中。


  漂亮的完成寄宿


  在这儿我的例子中,我将会概要的展示一个在Windows 控制台中自寄宿的服务 ,以及它在Microsoft中的web services和Internet Information Server (IIS)中寄宿。


  对于一个Windows控制台应用程序,它将会和声明一个我想寄宿在WCF 的ServiceHost中并把它为服务设置为被动数字神经端来监听到来的消息一样简单。建立一个ServiceHost实际上和建立一串在服务端的监听工厂一样,这些监听工厂和在用户端的通道工厂是等效的。在这个表中的代码将会建立一个在控制台程序中的WCF服务:


 Sub Main()
 Using svcHost As New ServiceHost(GetType(MyFirstSecuredWCFService))
  svcHost.Open()
  Console.WriteLine(“My First Secured WCF Service is running …”)
  Console.ReadLine()
  svcHost.Close()
 End Using
 End Sub


  为了把商业代码输出到IIS上,尽管IIS是服务宿主,我们只是简单的需要声明一个实体端点来和监听终端一样运行。在WCF中,默认的是.svc文件。在我们的例子中,我将会创建一个物理的index.svc文件并声明服务如下:


  <%@Service language=vb Debug=”true” class=”MyFirstSecuredWCFService” %>


  你可以从这个推断,我的商业代码仍然保持不变,尽管宿主容器的不断改变。


  下面我们将会主要检查服务的部署细节。因为深入的研究地址和邦定不是本篇文章关注的焦点,我将只会简单的展示一个地址和它的邦定将如何的在一个配置文件中容易的被定义。这也可以通过代码来配置,但是会使得大量的灵活性的折衷。请想象一下:如果一个末端的地址改变了,管理员将能够更改那个终端的配置文件而不需要修改和编辑任何的代码。


  邦定的部分将会在后面的安全部分解释。下面表中所展示的是服务和契约是如何同它的监听地址缠绕在一起的。换一句话说,这个配置细节仅仅涉及到由开发者起草抽象契约,然后标识它所位于的集合并为它分配一个众所周知的通讯终端地址给它。


  <system.serviceModel>
 <services>
  <service type=”MyFirstSecuredWCFService, WebApp”>
  <endpoint address=”http://localhost:2088/MyFirstSecuredWCFService”
 contract=”IMyFirstSecuredWCFService, WebApp” binding=… />
  </service>
 </services>
 …
 </system.serviceModel>


  请注意我是如何把契约(第二个表)和它的实现类(第三个表)联系起来的,在这儿被归之于一个endpoint address的一个service type。


  对于寄宿于IIS的应用程序,终端地址应被物理文件*.svc和它所在的虚拟文件夹定义,并且应该有一个空值,除非你给它一个扩展并且为它赋予一个不同的绑定集合。例如,如果endpoint address=””,那么实际上的通讯终端是http://yourSite/yourDirectory/yourfile.svc 。如果我设置为endpoint address=”secure”并把它绑定到相同的契约,但是具有一个不同的绑定集合(例如,一个安全的绑定集合),消费者将需要到http://yourSite/yourDirectory/yourfile.svc/secure下去初始化一个同服务的安全的消息交换。不要让这个uri误导了你,虽然它是有一些不自然,但是它并没有错。这只是IIS和整个ASP.NET / ASMX架构所采用的方式,通过定义一个物理文件(例如, *.asmx)作为聚焦的进入端。


  请一定记住的是,我在这儿谈到的是一些WCF终端,并不是物理网络节点。一个相同的物理网络节点可以寄宿很多的WCF终端和任意多的那个节点的契约。那将会是以后某一天的话题。


  探戈需要两个人


  一个服务是被动的并且它本身并不能做什么。那就是为什么有一个终端地址被赋予它,以使得它能够被外部世界所访问。在服务被正确的建立之后,这个难题的最后一块就是建立和配置消费的客户端。


  现在我们所拥有的就是一个命令提示符的工具,称之为svcutil.exe,它用来构建代理和客户端相应的基于发布的服务的元数据(WSDL + Schema + Policy)的配置文件。这将会是WCF团队的目标,希望能够在以后的Visual Studio版本中集成进去这个客户元数据消费者。它应该是类似约一些现存的称之为”Add Web Reference”的人造物或者我比较倾向于称之为”Add Service”的效果的东西。


  一旦服务建立起来,运行并监听,我们用服务终端地址运行svcutil.exe。例如有一个寄宿于IIS的服务,它将会是


  svcutil.exe https://localhost/Softwaremaker/Projects/index.svc


  如果我在一个基于控制台运行它,它将会看起来像


  svcutil.exe http://localhost:2088/MyFirstSecuredWCFService.


  这都取决于我如何建立虚拟文件夹和我是把地址加入到代码中还是在配置文件中。


  输出文件将会有两个文件。


  1. 一个由选定的语言生成的WCF代理,它把方法调用转换为分发的消息。


  2. 一个为客户端实现的app.config文件,它对应于服务端的具体的技术部署细节的配置文件。


  在这以后,它将只会使用客户代码来用生成的WCF代理来进行方法调用。


  Sub Main()
 Console.WriteLine(“Press enter when the Credit Card Service is running …”)
 Console.ReadLine()
 Dim pxy As New MyFirstSecuredWCFServiceProxy


 Dim cc As New CreditCard
 cc.AccountName = “William Tay”
 cc.CreditCardExpiryDate = “08082008”
 cc.CreditCardNo = “123456780”
 cc.CreditCardType = “AMEX”
 Console.WriteLine(pxy.SubmitCreditCard(cc))
 Console.ReadLine()
 End Sub
End Module


  当我承认上 面所展示的代码有一些RPC的气息——只是有它的味道,而没有错误——WCF正在发送消息,并且能正确的向它的核发送消息。上面的例子就是一个简单的直接的途径,这是现在的大多数开发者都能识别的。


  在电缆上的作为结果而发生的分发消息是一种良好格式的SOAP消息,而WCF服务将会以相同的格式的结果来响应。


  <s:Envelope >
    <s:Header />
    <s:Body>
      <SubmitCreditCard http://demos.softwaremaker.net/MyFirstSecuredWCFService”>http://demos.softwaremaker.net/MyFirstSecuredWCFService“>
        <crdcard >
          <a:AccountName>William Tay</a:AccountName>
          <a:CreditCardExpiryDate>08082008</a:CreditCardExpiryDate>
          <a:CreditCardNo>123456780</a:CreditCardNo>
          <a:CreditCardType>AMEX</a:CreditCardType>
        </crdcard>
      </SubmitCreditCard>
    </s:Body>
  </s:Envelope>

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐