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
消息
优点:
o
WSDL
定义仍然像
RPC/Encoded
样式一样简单直接。
o
操作名仍然出现在
SOAP
消息中。
o
把类型编码从消息中排除了,因此提升了吞吐性能。
缺点:
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
消息
下面就是对这种样式的优点和缺点的总结。
优点:
o
在
SOAP
消息中没有类型编码信息。
o
你总能用任何
XML
验证器来验证消息,在
soap
体中任何东西都在
schema
中有定义。
o
使用
document
样式,规则不是那么严格,还有对
XML Schema
进行增强和更改时不会破坏接口。
o
如果使用某特殊序列进行多进程调用,
Document
样式可以保持应用程序的状态。
o
Document
样式更加适合异步处理。
o
许多
document-messaging
服务能够选择文档的
DOM
和
SAX
两种处理方式的其中一种,结果就是能最小化在内存中的处理。
缺点:
o
WSDL
定义变得更加复杂。
o
在
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.