Web 服务最佳实践

 
Web 服务最佳实践
概要
Web 服务是作为一种沟通技术而被很好地制订出来,它为 Internet 提供最佳的互通能力。它们的标准化进程正高速地进行着,这必将引起它们会被更广泛的接受。尽管如此,从许多邮件列表、用户组和各种讨论来判断,在不同 Web 服务设计( Web Service Design )方法中仍然存在相当多的混乱情形。 “Document/Literal” 意味着什么,而 “RPC-style” 又是什么,怎样使 SOAP“message-style” 适合这呢?
这篇文章将会阐明和详细解释那些由 Web 服务标准化组定义的不同的 Web 服务设计方法学,阐明各种术语,着重解释它们的不同之处。在这里,我们将把精力放在如下的几个 Web 服务设计方法学,评估它们的优点和缺点,并在设计一种互通性 Web 服务中探究每一种类型得到多大程度的支持。
1.             RPC/Encoded 样式
2.             RPC/Literal 样式
3.             Document/Literal 样式
4.             Document /Literal 外覆样式
介绍
在市场上 Web 服务在相对存在较短的时间内,得到了巨大的认同和广泛的应用。其中一个主要原因当然是它们的那些非常早期的开放式标准,而这些标准就是市场上所有主要的 Web 服务推崇者所推动的;另一方面,在 Web 服务看起来应该像什么和他们应该如何通信这些方面,这些推崇者各有自己的偏爱。这已经导致今天的标准支持各种不同的关于 web 服务消息能怎样格式化和它们如何通信的方法,事实上,那些不同的通信类型是需要的。
这些描述和使用 Web 服务的相关标准是 WSDL(Web 服务描述语言 ) ,一种标准的类似 XML   Schema 的语言,用它详细说明 Web 服务和简单对象访问协议( SOAP ), Web 服务使用的实际的沟通协议就是简单对象访问协议( SOAP )。在了解真正的设计方法学之前,让我们先阐述下在 web 服务领域中频繁使用到的各种术语。
沟通模式
让我们从沟通模式开始,在使用 Web 服务时我们应能本质地区别三种沟通方法:
·         远程进程调用: 客户端给服务提供者发送一个 SOAP 请求并等待一个 SOAP 响应(同步沟通)。
·         发送消息: 客户端发送一个 SOAP 请求但不期望有 SOAP 响应返回(单向沟通)。
·         异步回调: 一个客户端用上述方法中的一种调用服务。然后,双方为回叫调用交换角色。这种模式能建立在前面两种模式之上。
SOAP 协议格式化规则
现在我们转到一个 Web 服务的 SOAP 的消息(本质上是消息的 <soap:body> 元素)能如何格式化, WSDL1.1 区分两种不同绑定形式(参考 soap 的绑定形式): RPC Document (文档)。(译者注: RPC (消息包含参数并返回值) Document (消息包含文档))
·               RPC 样式
RPC 样式指定 <soap:body> 元素包含一个将被调用的 web 方法的名称的元素( wrapper element (封装元素))。这个元素依次为该方法的每个参数还有返回值作了记录。
·         Document  样式
如果是 document 样式,就没有像在 RPC 样式中的 wrapper 元素。转而代之的是消息片断直接出现在 <soap:body> 元素之下。没有任何 SOAP 格式化规则规定 <soap:body> 元素下能包含什么;它包含的是一个发送者和接收者都达成一致的 XML 文档。
第二种格式规则就是 ‘Use’ 属性。这与各种类型如何在 XML 中显示有关,它指定使用某种编码规则对消息片段进行编码,还是使用消息的具体架构来定义片段。如下就是提供的两种选择:
·   编码
如果 use 的值是 encoded”, 则每个消息片段将使用类型属性来引用抽象类型。通过应用由 encodingStyle 属性所指定的编码样式,可使用这些抽象类型生成具体的消息。最常用到的 SOAP 编码样式是在 SOAP1.1 中定义的一组序列化规则,它说明了对象、结构、数组和图形对象应该如何序列化。通常,在应用程序中使用 SOAP 编码着重于远程进程调用和以后适合使用 RPC 消息样式。
·   Literal
如果 use 的值是 Literal 则每个片段使用 element 属性(对于简单片段)或 type 属性(对于复合片段)来引用具体架构,例如,数据根据指定的架构来序列化,这架构通常使用 W3C XML 架构来表述。
1 总结了各种不同的 web 服务参数的可选项。一个重要的事实就是,每个 web 服务开发人员要做三个独立的决定,使用什么 沟通模式 ?什么 SOAP 格式化 样式 ?最后用到什么 SOAP 消息编码类型?
 
WSDL 参数
可用选项
Communication Patterns
远程进程调用或单方消息发送
Style
Document RPC
Use
Encoded Literal
1.Web 服务参数。
虽然,理论上说,这些选项的任何一种组合都是可以的,但在实践中会明确偏爱某种组合而不是其它的,同时,各种标准和 Web 服务互用性组织( WS-I )也有某种明确的偏爱。
因此在实践中,仅 Document/Literal RPC/Encoded 得到了广泛的应用,同时也被大部分平台直接支持,这些平台显示在表 2 中。这个表也显示了对不同的 style/use 组合的 WS-I 一致性测试结果。
Style/Use 组合
支持的 Soap 工具
WS-I 一致性
RPC/Encoded
Microsoft, Axis 1.1
Failed
RPC/Literal
Axis 1.1
Failed
Document/Literal
Microsoft , Axis1.1
Passed
2. Web 服务格式支持
由于 Document/Encoded 这种组合不支持现有使用的平台,所以没有测试。事实上 Document/Encoded 组合还没有真实的应用环境。
一个简单 Web 服务例子
现在我们来更详细的了解被使用和支持最多的 RPC/Encoded Document/Literal style/use 组合。我们将利用一个叫做 “SendTemperature” web 方法举例来说明每一种的 style/use 组合,那个 web 方法使用的参数是一个用户自定义的复杂对象,返回一个 void 类型,这些在清单 1 中有描述。
我们选择了这么一个使用复杂数据类型例子,就是为了让各种样式之间的不同更加明显。
public  void  SendTemperature (Temperature[] TempCollection){}

public  class  Temperature 

{

   
///  <remarks/>

   
public  int  Id;

  
///  <remarks/>

  
public  string  Name;

  
///  <remarks/>

  
public  System.Double Temperature;

}
清单 1. 使用 C# 实现的 web 方法 SendTemperature
针对这个 web 方法,我们将展示各种 web 服务样式就它们各自的 SOAP 请求形式怎样在 WSDL 中中实现,重点讲述它们的不同之处。我们利用 Microsoft VS.NET Axis SOAP 工具箱来实现。
注意,为了简单,在这篇文章中我们忽略了 WSDL 文件的名称空间、前缀和服务部分。清单 2 列出了常用的名称空间和前缀。
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 

xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 

xmlns:s="http://www.w3.org/2001/XMLSchema" 

xmlns:s0="http://interop.webservices.fhso.ch/{service name}" 

xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 

xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 

xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 

xmlns="http://schemas.xmlsoap.org/wsdl/" 

targetNamespace="http://interop.webservices.fhso.ch/{service name}/ ”
清单 2. 名称空间和使用到的前缀
1.             RPC/Encoded 样式
实质上 RPC/Encoded 是一种典型的遵循 远程进程调用 模式的样式,在这种模式中客户端发送一个同步请求给服务器来执行一次操作。 SOAP 请求包含了要执行的方法的名称和它携带的参数。运行 web 服务的的服务器把该请求转化成适当的对象,然后执行操作并向客户端做出响应反馈一个 SOAP 消息。在客户端,该响应被用来合成一个适当的对象并返回给客户端所需要的的信息。在 RPC 样式的 web 服务中,整个方法在 WSDL 文件和 SOAP 体中被指定,包含方法的发送参数和返回值。因此我们用这种样式会有相当紧密的耦合关系。
清单 3 显示在 RPC/Encoded 样式中 SendTemperature WSDL 定义。
< types >

< s:schema  targetNamespace ="http://interop.webservices.fhso.ch/rpcencoded">

< s:complexType  name ="ArrayOfTemperature">

< s:complexContent  mixed ="false">

< s:restriction  base ="soapenc:Array">

< s:attribute  d7p1:arrayType ="s0:Temperature[]"  ref ="soapenc:arrayType"  

xmlns:d7p1
="http://schemas.xmlsoap.org/wsdl/"/>

</ s:restriction >

</ s:complexContent >

</ s:complexType >

< s:complexType  name ="Temperature">

< s:sequence >

< s:element  minOccurs ="1"  maxOccurs ="1"  name ="Id"  type ="s:int"/>

< s:element  minOccurs ="1"  maxOccurs ="1"  name ="Name"  type ="s:string"/>

< s:element  minOccurs ="1"  maxOccurs ="1"  name ="value"  type ="s:double"/>

</ s:sequence >

</ s:complexType >

</ s:schema >

</ types >

< message  name ="SendTemperatureSoapIn">

< part  name ="Collection"  type ="s0:ArrayOfTemperature"/>

</ message >

< message  name ="SendTemperatureSoapOut"/>

< portType  name ="TemperatureRpcEncodedSoap">

< operation  name ="SendTemperature">

< input  message ="s0:SendTemperatureSoapIn"/>

< output  message ="s0:SendTemperatureSoapOut"/>

</ operation >

</ portType >

< binding  name ="TemperatureRpcEncodedSoap"  type ="s0:TemperatureRpcEncodedSoap">

< soap:binding  style ="rpc"  transport ="http://schemas.xmlsoap.org/soap/http"/>

< operation  name ="SendTemperature">

< soap:operation  soapAction ="http://interop.fhso.ch/soapformat/SendTemperature"/>

< input >

< soap:body  use ="encoded"  

      encodingStyle
="http://schemas.xmlsoap.org/soap/encoding/"/>

</ input >

< output >

< soap:body  use ="encoded"  

      encodingStyle
="http://schemas.xmlsoap.org/soap/encoding/"/>

</ output >

</ operation >

</ binding >
清单 3. SendTemperature RPC/Encoded 样式中的 SOAP 定义
注意绑定的 style 的值被设为 ‘rpc’ use 的值是 ‘encoded’. <message> 这节中,可以有任意数目的 <part> 元素,该元素包含一个 type 属性,对 ‘rpc’ 样式来说,该属性的值是惟一的。现在我们看看调用这个 SendTemperature web 方法会发生什么,传送的参数是包含两个元素的数组。清单 4 显示了 SOAP 消息形式的结果。
 
< soap:Envelopexmlns:

     
soap ="http://schemas.xmlsoap.org/soap/envelope/"  

     xmlns:soapenc
="http://schemas.xmlsoap.org/soap/encoding/"  

     xmlns:tns
="http://interop.webservices.fhso.ch/rpcencoded"  

     xmlns:types
="http://interop.webservices.fhso.ch/rpcencoded/encodedTypes"  

     xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"  

     xmlns:xsd
="http://www.w3.org/2001/XMLSchema">

< soap:Body  soap:encodingStyle ="http://schemas.xmlsoap.org/soap/encoding/">

< SendTemperature >

< Collection  href ="#id1"/>

</ SendTemperature >

< soapenc:Array  id ="id1"  soapenc:arrayType ="tns:Temperature[2]">

< Item  href ="#id2"/>

< Item  href ="#id3"/>

</ soapenc:Array >

< tns:Temperature  id ="id2"  xsi:type ="tns:Temperature">

< Id  xsi:type ="xsd:int"> 3 </ Id >

< Name  xsi:type ="xsd:string"> Station1 </ Name >

< value  xsi:type ="xsd:double"> 34.3 </ value >

</ tns:Temperature >

< tns:Temperature  id ="id3"  xsi:type ="tns:Temperature">

< Id  xsi:type ="xsd:int"> 56 </ Id >

< Name  xsi:type ="xsd:string"> Station3 </ Name >

< value  xsi:type ="xsd:double"> 23.6 </ value >

</ tns:Temperature >

</ soap:Body >

</ soap:Envelope >
清单4 . SendTemperature RPC/Encoded 样式中的 SOAP 消息
SOAP 消息中每个参数都会被类型编码(译者注:例如 xsi:type="xsd:int" ),注意,在 SOAP 消息中的 ‘href’ tags 标签,实质上也是 RPC/Encoded 样式的一部分,它被用来查询数组中的元素。在任何 literal 样式中,这个 ‘href’ 标签是不可用的。让我们来分析下 WSDL 定义和它的 SOAP 消息形式。
优点:
o        WSDL 文件的定义遵循直观和众所周知的远程进程调用的沟通模式。
o        操作名显示在消息中,因此接收者很容易就把消息分派给它的实现。
o        如果你正在你的服务中使用数据图形或者多态,在本文描述的样式中这是惟一能使用的样式。
缺点:
o        SOAP 消息包含的类型编码信息就如 xsi:type="xsd:int", xsi:type="xsd:string", xsi:type="xsd:double" ,这些就是一种开销。
o        通常验证 SOAP 消息是很困难的。
o        RPC 样式引起了一种在服务提供者和客户之间的紧密耦合,任何对接口的更改都会导致服务和客户间联系的中断。
o        依赖那也许也许要同步处理的信息,内存约束也许使得 RPC 消息传输不能实现,因为在内存中发生消息聚集。
o        不被 WSI 一致性标准所支持。
现在我们来看同样的 web 方法用 RPC/Literal 样式实现并看看它是否能消除 RPC/Encode 样式中的一些缺陷。
2.       RPC/Literal 样式
这里的 WSDL 定义看起来和先前的非常相似,惟一可预见的改变就是 <soap:body> 元素中 ‘use’ 的值由 ‘encoded’ 变为 ‘literal’ ,这显示在清单 5 中。就如上面所描述的,这就意味着数据类型定义并是由引用的 XML Schema 所提供,而不是 RPC 编码。
 
< types >

< s:schema  elementFormDefault ="qualified"  

targetNamespace
="http://interop.webservices.fhso.ch/rpcliteral">

<!-- there are no global element declarations. There's nothing in the 

schema that completely describes the content of soap:Body -->


< s:complexType  name ="ArrayOfTemperature">

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="unbounded"  name ="Temperature"  

     nillable
="true"  

     type
="s0:Temperature"/>

</ s:sequence >

</ s:complexType >

< s:complexType  name ="Temperature">

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Id"  type ="s:int"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Name"  type ="s:string"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="value"  type ="s:double"/>

</ s:sequence >

</ s:complexType >

</ s:schema >

</ types >

< message  name ="SendTemperatureSoapIn">

< part  name ="Collection"  type ="s0:ArrayOfTemperature"/>

</ message >

< message  name ="SendTemperatureSoapOut"/>

< portType  name ="TemperatureRpcLiteralSoap">

< operation  name ="SendTemperature">

< input  message ="s0:SendTemperatureSoapIn"/>

< output  message ="s0:SendTemperatureSoapOut"/>

</ operation >

</ portType >

< binding  name ="TemperatureRpcLiteralSoap"  

            type
="s0:TemperatureRpcLiteralSoap">

< soap:binding  style ="rpc"  

            transport
="http://schemas.xmlsoap.org/soap/http"/>

< operation  name ="SendTemperature">

< soap:operation 

            
soapAction ="http://interop.fhso.ch/soapformat/SendTemperature"/>

< input >

< soap:body  use ="literal"  

            namespace
="http://interop.fhso.ch/soapformat/SendTemperature"/>

</ input >

< output >

< soap:body  use ="literal"  />

</ output >

</ operation >

</ binding >
清单 5. SendTemperature RPC/Literal 样式中的 SOAP 定义
然而,使用这种样式仍然存在一个主要的缺陷。独立的 XML Schema 不会告诉你消息体中的信息集合包含了些什么,你也必需知道 RPC 规则。因此,该 schema 描述的一种 RPC/Literal 消息但并不足以验证那种消息。
在清单 6 中让我们看看针对 RPC/Literal 样式的 SOAP 消息形式。注意,类型编码被完全移除掉了。
 
< soapenv:Envelope

xmlns:soapenv ="http://schemas.xmlsoap.org/soap/envelope/"  

xmlns:xsd
="http://www.w3.org/2001/XMLSchema"  

xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance">

< soapenv:Body >

<  ! – SendTemperature  is the name of the procedure being invoked. 

Collection is a parameter of that procedure.

Note that Collection is not namespace qualified. The two Temperature 

elements are contents of the Collection parameter. This Collection can 

be thought of as an array of Temperature items. Note that the Temperature 

is namespace qualified but is in a different namespace than SendTemperature. 

These namespace rules are unique to RPC-style messages -- 
>  

< SendTemperature  xmlns ="http://interop.fhso.ch/soapformat/SendTemperature">

< Collection  xmlns ="">

< ns1:Temperature  xmlns:ns1 ="http://interop.webservices.fhso.ch/rpcliteral">

< ns1:Id > 2 </ ns1:Id >

< ns1:Name >  Station1 </ ns1:Name >

< ns1:value > 34.2 </ ns1:value >

</ ns1:Temperature >

< ns2:Temperature  xmlns:ns2 ="http://interop.webservices.fhso.ch/rpcliteral">

< ns2:Id > 56 </ ns2:Id >

< ns2:Name >  Station 3 </ ns2:Name >

< ns2:value > 23.6 </ ns2:value >

</ ns2:Temperature >

</ Collection >

</ SendTemperature >

</ soapenv:Body >

</ soapenv:Envelope >
清单 6. SendTemperature RPC/Literal 样式中的 SOAP 消息
优点:
WSDL 定义仍然像 RPC/Encoded 样式一样简单直接。
操作名仍然出现在 SOAP 消息中。
把类型编码从消息中排除了,因此提升了吞吐性能。
缺点:
o            服务和客户之间仍然有紧密耦合。
o            仍然难以用 SOAP 消息来验证传输的数据。
o            它也不被 WSI 一致性标准所支持。
现在,我们来看看 Document/Literal 样式,来看一下该样式是否能减少现有的缺陷。
3.       Document/Literal 样式
Document 样式和上面的 RPC 样式最主要的不同就是,前者中客户在一个规范的 XML 文档中向服务器发送服务参数,而代替了后者中的一组离散的方法的参数值。这使得 Document 样式比 RPC 样式有更加松散的耦合关系。
Web 服务提供者处理规范的 XML 文档,执行操作并向客户端作出响应,返回的也是一个规范的 XML 文档。在服务器对象(参数,方法调用等)和 XML 数据值之间并没有一种直接的映射关系。应用程序负责映射 XML 数据值。 Document 样式中 SOAP 消息在它的 SOAP 体中包含了一个或者更多的 XML 文档。协议并没有约束文档需要如何组织构成;这完全是在程序级处理的。另外, Document 样式 web 服务遵循异步处理范例。
就像在清单 7 中显示的那样,相比 RPC 样式,该样式的 WSDL 定义有了很大的改变。
< types >

< s:schema  elementFormDefault ="qualified"  

     targetNamespace
="http://interop.webservices.fhso.ch/docLit">

<  ! -  - this element declaration describes the entire contents 

of the soap:Body in the request message.

This is a feature of document/Literal that RPC/Literal lacks - -
>  

< s:element  name ="Collection">

< s:complexType >

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="unbounded"  name ="Temperature"  

   nillable
="true"  

   type
="s0:Temperature"/>

</ s:sequence >

</ s:complexType >

</ s:element >

< s:complexType  name ="Temperature">

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Id"  type ="s:int"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Name"  type ="s:string"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="value"  type ="s:double"/>

</ s:sequence >

</ s:complexType >

<  ! – Similarly  this element declaration describes 

the contents of the soap body in the response 

message. In this case the response is empty -- 
>  

< s:element  name ="SendTemperatureResponse">

< s:complexType />

</ s:element >

</ s:schema >

</ types >

< message  name ="SendTemperatureSoapIn">

< part  name ="parameters"  element ="s0:Collection"/>

</ message >

< message  name ="SendTemperatureSoapOut">

< part  name ="parameters"  element ="s0:SendTemperatureResponse"/>

</ message >

< portType  name ="TemperatureDocLitSoap">

< operation  name ="SendTemperature">

< input  message ="s0:SendTemperatureSoapIn"/>

< output  message ="s0:SendTemperatureSoapOut"/>

</ operation >

</ portType >

< binding  name ="TemperatureDocLitSoap"  type ="s0:TemperatureDocLitSoap">

< soap:binding  style ="document"  

  transport
="http://schemas.xmlsoap.org/soap/http"/>

< operation  name ="SendTemperature">

< soap:operation  soapAction =

   "http://interop.webservices.fhso.ch/documentliteral/SendTemperature"
 

style
="document"/>

< input >

< soap:body  use ="literal"  />

</ input >

< output >

< soap:body  use ="literal"  />

</ output >

</ operation >

</ binding >
清单 7. SendTemperature Document/Literal 样式中 WSDL 定义 .
注意,绑定 style 的值是 document’ 以及 use 的值是 literal’. message’ 这一节中,仅可能有一个 <part> 元素,该元素包含了一个叫做 element 的属性。
这片断指出一个描述了 soap 体的全部内容的 schema 元素申明。注意,现在集合被定义为一个元素而不上一种类型。 Document/Literal 样式的主要特点,相比 RPC/Literal 样式的关键有益之处就是 schema 元素申明完全描述了 <soap:body> 的内容。这样就意味着你只通过查看 schema 而不要任何附加规则就能说出消息体信息集中包含了什么。因此你就能接收 schema 描述一个 Document/Literal 消息并用它验证消息,这可是用 RPC/Literal 样式无法完成的。
现在让我们来看看在清单 8 中该样式相应的 SOAP 消息形式。注意,没有指定类型编码数据,还有操作名也消失了。
 
< soap:Envelope 

xmlns:soap ="http://schemas.xmlsoap.org/soap/envelope/"  

xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"  

xmlns:xsd
="http://www.w3.org/2001/XMLSchema">

< soap:Body >

<  --  note that the Operation name is missing in the message --  >

< Collection  xmlns ="http://interop.webservices.fhso.ch/docLit">

< Temperature >

< Id > 2 </ Id >

< Name > Station 1 </ Name >

< value > 34.2 </ value >

</ Temperature >

< Temperature >

< Id > 56 </ Id >

< Name > Station 3 </ Name >

< value > 23.5 </ value >

</ Temperature >

</ Collection >

</ soap:Body >

</ soap:Envelope >
清单 8. SendTemperature   Document/Literal 样式中 SOAP 消息
下面就是对这种样式的优点和缺点的总结。
优点:
SOAP 消息中没有类型编码信息。
你总能用任何 XML 验证器来验证消息,在 soap 体中任何东西都在 schema 中有定义。
使用 document 样式,规则不是那么严格,还有对 XML Schema 进行增强和更改时不会破坏接口。
如果使用某特殊序列进行多进程调用, Document 样式可以保持应用程序的状态。
Document 样式更加适合异步处理。
许多 document-messaging 服务能够选择文档的 DOM SAX 两种处理方式的其中一种,结果就是能最小化在内存中的处理。
缺点:
WSDL 定义变得更加复杂。
SOAP 消息中的操作名没有了,没有了名称,把消息分派给它的实现方法就变得困难或不可能了。
我们已经看到 Document/Literal 样式消除了许多 RPC/Literal 样式中的缺点,另一方面,它也引入了一个新的缺点:在 SOAP 消息中丢失了操作名。
由于第四种样式选项, Document/Encoding 样式,没有实际使用,将不对它进行描述了。而是我们将看看 Document/Literal 样式的扩展,即 Document/Literal 外覆样式。
4.       Document/Literal 外覆样式
该样式被微软推荐来消除因标准的 Document/Literal 样式引起的缺点。虽然在 WSDL1.1 标准中没有对该样式进行说明,但现在许多 SOAP 工具都支持它。
让我们看清单9中的 WSDL 定义,以及清单10中相应的 SOAP 消息。
 
< types >

< s:schema  elementFormDefault ="qualified"  

targetNamespace
="http://interop.webservices.fhso.ch/docLitWrapped">

< s:element  name ="SendTemperature">

< s:complexType >

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Collection"  

type
="s0:ArrayOfTemperature"/>

</ s:sequence >

</ s:complexType >

</ s:element >

< s:complexType  name ="ArrayOfTemperature">

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="unbounded"  name ="Temperature"  

  nillable
="true"  

  type
="s0:Temperature"/>

</ s:sequence >

</ s:complexType >

< s:complexType  name ="Temperature">

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Id"  type ="s:int"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Name"  type ="s:string"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="value"  type ="s:double"/>

</ s:sequence >

</ s:complexType >

< s:element  name ="SendTemperatureResponse">

< s:complexType />

</ s:element >

</ s:schema >

</ types >

< message  name ="SendTemperatureSoapIn">

< part  name ="parameters"  element ="s0:SendTemperature"/>

</ message >

< message  name ="SendTemperatureSoapOut">

< part  name ="parameters"  element ="s0:SendTemperatureResponse"/>

</ message >

< portType  name ="TemperatureDocLitWrappedSoap">

< operation  name ="SendTemperature">

< input  message ="s0:SendTemperatureSoapIn"/>

< output  message ="s0:SendTemperatureSoapOut"/>

</ operation >

</ portType >

< binding  name ="TemperatureDocLitWrappedSoap"  

   type
="s0:TemperatureDocLitWrappedSoap">

< soap:binding  style ="document"  

   transport
="http://schemas.xmlsoap.org/soap/http"/>

< operation  name ="SendTemperature">

< soap:operation  soapAction =

  "http://interop.webservices.fhso.ch/docLitWrapped/SendTemperature"
 

  style
="document"/>

< input >

< soap:body  use ="literal"/>

</ input >

< output >

< soap:body  use ="literal"/>

</ output >

</ operation >

</ binding >
清单 9. SendTemperature Document/Literal 外覆样式中的 WSDL 定义 .
首先,注意在清单9中操作名被重新引入到 WSDL 文件的第一个 element’ 标签中,也注意到,在清单10中 SOAP 消息看起来跟 RPC/Literal 样式的 SOAP 消息类似,但是有一个细微的差别,在 RPC/Literal 样式 SOAP 消息中 <soap:body> 元素的子元素 <SendTemperature> 是操作名,而在 Document/Literal 外覆样式的 SOAP 消息中 <SendTemperature> 是元素名,单一输入消息片断引用该元素,该样式用一种狡猾的方法把操作名重新引入到 SOAP 消息中。
这些就是 document/Literal 外覆样式的主要特征 :
§   输入消息有一单一片断
§   该片断是一个元素
§   该元素同操作有相同的名字
§   该元素的复杂类型没有属性
 
< soap:Envelope 

xmlns:soap ="http://schemas.xmlsoap.org/soap/envelope/"  

xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"  

xmlns:xsd
="http://www.w3.org/2001/XMLSchema">

< soap:Body >

<!  – the following is an xml document described in the service’s 

contract using XML schema. In this case SendTemperature may or may not 

be the name of the remote procedure being invoked by this message. 

Also, Collection may or may not be the name of the parameter. We know the 

structure of the xml document but We don’t know how the service 

is going to process it -- 
>

< SendTemperature  xmlns ="http://interop.webservices.fhso.ch/docLitWrapped">

< Collection >

< Temperature >

< Id > 2 </ Id >

< Name > Station 1 </ Name >

< value > 34.2 </ value >

</ Temperature >

< Temperature >

< Id > 56 </ Id >

< Name > Station 3 </ Name >

< value > 23.6 </ value >

</ Temperature >

</ Collection >

</ SendTemperature >

</ soap:Body >

</ soap:Envelope >
清单 10. SendTemperature Document/Literal 外覆样式中的 WSDL 消息 .
下面就是该方法缺点与优点的总结:
优点:
§   包含了所有 Document/Literal 样式的优点。
§   操作名出现在 SOAP 消息中。
缺点:
§   即使 WSDL 定义变得更加复杂,但仍然有不少缺点。
§   如果你在 web 服务中重载了操作,你就不能使用该样式。
到现在为止,我们看到 Document/Literal Document/Literal 外覆样式相比于任何其它样式,带给我们许多的弹性和优点,但是仍存在一些有待解决的问题。
Document/Literal Document/Literal 外覆样式的局限性
假设,我们如清单 11 中显示的那样重载了操作。
public void SendTemperature (Temperature[] TempCollection){}
public void SendTemperature (Temperature[] TempCollection, int week){}
清单 11. 重载 SendTemperature 方法 .
在这种情况,就不可能使用 Document/Literal 外覆样式,即使 WSDL 规范允许有重载的方法。原因就是,当我们在一个 WSDL 文档中增加一个外覆样式时,你将需要一个跟操作相同名字的元素(请看清单9)。当我们想在 XML Schema 有两个同名称的元素时问题就出现了。因此,我们为了重载的操作的来个二选一,要么寻找非 Ddocument/Literal 外覆样式,要么一种 RPC 样式。
让我们看看我们怎么在 Document/Literal 样式中实现它,这儿在 WSDL 定义的 schema 节为上述的两个 web 方法有了修改。
 
< types >

< s:schema  elementFormDefault ="qualified"  

   targetNamespace
="http://interop.webservices.fhso.ch/docLit">

< s:element  name ="Collection">

< s:complexType >

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="unbounded"  name ="Temperature"  

   nillable
="true"  type ="s0:Temperature"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="week"  type ="s:int"/>

</ s:sequence >

</ s:complexType >

</ s:element >

< s:complexType  name ="Temperature">

< s:sequence >

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Id"  type ="s:int"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="Name"  type ="s:string"/>

< s:element  minOccurs ="0"  maxOccurs ="1"  name ="value"  type ="s:double"/>

</ s:sequence >

</ s:complexType >

< s:element  name ="SendTemperatureResponse">

< s:complexType />

</ s:element >

</ s:schema >

</ types >
清单 12. SendTemperature WSDL 定义的 Schema 节作了更改
我们仅向集合中添加了另一个元素,所有其它的跟清单 7 中保持相似。有趣的是让我们看看用 VS.NET wsdl.exe 实用工具生成的客户端代理。
 
Public  void  SendTemperature([System.Xml.Serialization.XmlElementAttribute("Protocol", 

                IsNullable=
true )] Temperature[] Temperature,  int  week, 

                [System.Xml.Serialization.XmlIgnoreAttribute()] 
bool  weekSpecified) {

this .Invoke("SendTemperature",  new  object [] {

   Temperature,

   week,

   weekSpecified});

}
清单 13. 为重载操作 SendTemperature 生成的 C# 代理代码
注意到有另外一个名称为 weekSpecified Boolean 型参数被自动创建,现在客户端就能用两种方法调用这个重载操作的 web 方法,如果一个客户端调用的方法有 week 参数以及 weekSpecified 的值设为 “false” 时,我们调用的是清单 11 中的第一个方法,还有 SOAP 请求也跟清单 10 中的一种相同。
 
SendTemperature(Temperatue_Object,7, false );
 
另一方面客户端调用的方法 weekSpecified 的值被设为 “true” 时,那么它引用在清单 11 中的重载方法,以及现在的 SOAP 请求看起来就像清单 14 的那样,以一个 XML 标签来传递 “week” 参数。
 
SendTemperature(Temperature_Object,7, true );
 
< soap:Envelope 

xmlns:soap ="http://schemas.xmlsoap.org/soap/envelope/"  

xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"  

xmlns:xsd
="http://www.w3.org/2001/XMLSchema">

< soap:Body >

< Collection  xmlns ="http://interop.webservices.fhso.ch/docLit">

< Temperatue >

< Id > 2 </ Id >

< Name > Station 1 </ Name >

< value > 34.2 </ value >

</ Temperature >

< Temperature >

< Id > 56 </ Id >

< Name > Station 3 </ Name >

< value > 23.5 </ value >

</ Temperature >

< week > 7 </ week >

</ Collection >

</ soap:Body >

</ soap:Envelope >
清单 14. 第二个重载操作的 SOAP 消息形式
现在我们已经看到因 Document/Literal 外覆样式引起的问题怎样通过使用标准的 Document/Literal 样式而巧妙地解决了。
结论
Document-centric RPC 样式的 Web 服务提供了非常不同的应用环境, Document 样式的 web 服务更加适合于企业到企业在 Internet 上的交互,开发一个 Document 样式的 web 服务也许需要比 RPC 样式付出更多的努力。我们看到了 Document/Literal 样式和 Document/Literal 外覆样式相比于其它设计样式带给我们更多弹性和优点。任何时候,你不要对已经存在的 RPC 样式进行接口连接, document 样式的益处通常价值大于对服务进行接口连接。如果你设计web服务的主要需求是互用性,协调工作,那么 RPC/Encoded 样式就十分气馁了。
WS-I Basic profile 1.0 不提倡使用 RPC/Encoded 方法,推荐使用 Document/Literal 方法,还有 RPC/Literal 是惟一允许的 RPC 样式的 style/use 组合。许多人都认为在将来的 WSI-profile 版本中会屏弃 RPC/Literal 样式
参考
·         [WSI] WS-I Final Specification " BasicProfile Version 1.0a", 8 August 2003.
·         [WSDL] WSDL Specification.
·         [SOAP] SOAP Specification.
·         [IBMWS] IBM Developer works Web Services article suit.
·         [XMETH] Publicly available web services in both Doc/RPC styles at Xmethods.
·         [MSDN] Microsoft’s MSDN Web Services article library.
 
 
 

你可能感兴趣的:(Web 服务最佳实践)