Hello World
Java技术学习
posts - 17,  comments - 7,  trackbacks - 0
Web Service描述语言 WSDL 详解 2

SOAP消息

  对于使用WSDL的客户机和服务机来说,研究WSDL文件的一种方法就是决定什么来接受所发送的信息。尽管SOAP使用底层协议,如IP和HTTP等,但应用程序决定了服务器与客户机之间交互的高级协议。也就是说,进行一项操作,比如"echoint"把输入的整数送回,参数的数目、每个参数的类型、以及参数如何传送等因素决定了应用程序特定的协议。有很多方法可以确定此类协议,但我相信最好的方法就是使用WSDL。如果我们用这种视角来看待它,WSDL不只是一种接口协议,而且是一种协议特定的语言。它就是我们超越"固定"协议(IP、HTTP等)所需要的应用程序特定协议。

  WSDL可以确定SOAP消息是否遵从RPC或文档风格。RPC风格的消息(就是示例中所用的)看起来像是函数调用。而文档风格的消息则更普通,嵌套层次更小。下面的XML消息就是示例WSDL文件解析后的发送/接受效果,解析使用的是MS SOAP Toolkit 2.0(MSTK2)中的SoapClient对象。

  从客户端调用"foo(5131953)"函数:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 
<SOAP-ENV:Envelope 
  
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
  xmlns:SOAP-ENV
="http://schemas.xmlsoap.org/soap/envelope/">
 
<SOAP-ENV:Body>
  
<m:foo xmlns:m="http://tempuri.org/message/">
   
<arg>5131953</arg>
  
</m:foo>
 
</SOAP-ENV:Body>
  
</SOAP-ENV:Envelope>
 从服务器接受的信息:
  
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SOAP-ENV:Envelope 
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV
="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAPSDK1:fooResponse xmlns:SOAPSDK1="http://tempuri.org/message/">
<result>5131953</result>
</SOAPSDK1:fooResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

  两函数都调用了消息,其回应是有效的XML。SOAP消息由几部分组成,首先是<Envelop>元素,包含一个可选的<Header>元素以及至少一个<body>元素。Rpc函数所调用的消息体有一个根据操作"foo"命名的元素,而回应信息体有一个"fooResponse"元素。Foo元素有一个部分<arg>,就和WSDL中描述的一样,是单参数的。fooResponse也相应的有一个<result>的部分。注意encodingStyle、envelope和message的namespace和WSDL Bindings栏中的预定义的一致,重复如下:
<binding name="SimpleBinding" type="wsdlns:SimplePortType">
<stk:binding preferredEncoding="UTF-8" />
<soap:binding style="rpc" 
transport
="http://schemas.xmlsoap.org/soap/http"/>
<operation name="foo">
<soap:operation
soapAction="http://tempuri.org/action/Simple.foo"/>
<input>
<soap:body use="encoded" 
namespace
="http://tempuri.org/message/" 
encodingStyle
=
"http://schemas.xmlsoap.org/soap/encoding/"
 />
</input>
<output>
<soap:body use="encoded" 
namespace
="http://tempuri.org/message/" 
encodingStyle
=
"http://schemas.xmlsoap.org/soap/encoding/"
 />
</output>
</operation>
</binding>

WSDL的Types栏和Messages栏中的XML Schema
WSDL数据类型是基于"XML Schema: Datatypes"(XSD)的,现在已经被W3C推荐。这一文档共有三个版本(1999,2000/10,2001),因此必须在namespace属性的<definitions>元素中指明所使用的是哪一个版本。
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
在本文中,我将只考虑2001版本。WSDL标准的推荐者强烈建议使用2001版。

  在本栏和以后各部分,需使用以下简缩或前缀

前缀代表的Namespace描述
Soapenchttp://schemas.xmlsoap.org/soap/encodingSOAP 1.1 encoding
Wsdlhttp://schemas.xmlsoap.org/wsdl/soapWSDL 1.1
Xsd http://www.w3.org/2001/XMLSchemaXML Schema

  XSD基类型

  下表是直接从MSTK2文档中取出的,列举了MSTK2所支持的所有XSD基类型。它也告诉在客户端或服务器端的WSDL读取程序如何把XSD类型映射到在VB、C++和IDL中相应的类型。

XSD (Soap)类型变量类型 VBC++IDLComments
anyURIVT_BSTRStringBSTRBSTR 
base64Binary VT_ARRAY | VT_UI1Byte()SAFEARRAYSAFEARRAY(unsigned char) 
BooleanVT_BOOL Boolean VARIANT_BOOLVARIANT_BOOL 
ByteVT_I2Integershortshort转换时验证范围有效性
DateVT_DATEDateDATEDATE时间设为 oo:oo:oo
DateTimeVT_DATEDateDATEDATE 
DoubleVT_R8Doubledoubledouble 
DurationVT_BSTRStringBSTRBSTR不转换和生效
ENTITIESVT_BSTRStringBSTRBSTR不转换和生效
ENTITYVT_BSTRStringBSTRBSTR不转换和生效
FloatVT_R4Singlefloatfloat 
GDayVT_BSTRStringBSTRBSTR不转换和生效
GMonthVT_BSTRStringBSTRBSTR不转换和生效
GMonthDayVT_BSTRStringBSTRBSTR不转换和生效
GYearVT_BSTRStringBSTRBSTR不转换和生效
GYearMonthVT_BSTRStringBSTRBSTR不转换和生效
IDVT_BSTRStringBSTRBSTR不转换和生效
IDREFVT_BSTRStringBSTRBSTR不转换和生效
IDREFSVT_BSTRStringBSTRBSTR不转换和生效
IntVT_I4Longlonglong 
IntegerVT_DECIMALVariantDECIMALDECIMAL转换时范围生效
LanguageVT_BSTRStringBSTRBSTR不转换和生效
LongVT_DECIMALVariantDECIMALDECIMAL转换时范围生效
NameVT_BSTRStringBSTRBSTR不转换和生效
NCNameVT_BSTRStringBSTRBSTR不转换和生效
negativeIntegerVT_DECIMALVariantDECIMALDECIMAL转换时范围生效
NMTOKENVT_BSTRStringBSTRBSTR不转换和生效
NMTOKENSVT_BSTRStringBSTRBSTR不转换和生效
nonNegativeIntegeVT_DECIMALVariantDECIMALDECIMAL转换时范围生效
nonPositiveIntegerVT_DECIMALVariantDECIMADECIMAL转换时范围生效
normalizedStringVT_BSTRStringBSTRBSTR 
NOTATIONVT_BSTRStringBSTRBSTR不转换和生效
NumberVT_DECIMALVariantDECIMALDECIMAL 
positiveIntegerVT_DECIMALVariantDECIMALDECIMAL转换时范围生效
QnameVT_BSTRStringBSTRBSTR不转换和生效
ShortVT_I2Integershortshort 
StringVT_BSTR StringBSTRBSTR 
TimeVT_DATEDateDATEDATE日设为1899年12月30日
TokenVT_BSTRStringBSTRBSTR不转换和生效
unsignedByteVT_UI1Byteunsigned charunsigned char 
UnsignedIntVT_DECIMALVariantDECIMALDECIMAL转换时范围生效
unsignedLongVT_DECIMALVariantDECIMALDECIMAL转换时范围生效
unsignedShortVT_UI4LongLongLong转换时范围生效

  XSD定义了两套内建的数据类型:原始的和派生的。在下文中查阅内建数据类型的层次十分有益:
http://www.w3.org/TR/2001/PR-xmlschema-2-20010330 

complex类型

  XML schema允许complex类型的定义,就像C里是struct。例如,为了定义类似如下的C的struct类型:
typedef struct {
 
string firstName;
 
string lastName;
 
long ageInYears;
 
float weightInLbs;
 
float heightInInches;
} PERSON;

我们可以写XML schema:
<xsd:complexType name="PERSON">
<xsd:sequence>
 
<xsd:element name="firstName" type="xsd:string"/>
 
<xsd:element name="lastName" type="xsd:string"/>
 
<xsd:element name="ageInYears" type="xsd:int"/>
 
<xsd:element name="weightInLbs" type="xsd:float"/>
 
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
  不过,complex类型可以表达比struct更多的信息。除了<sequence>以外,它还可以有其他的子元素,比如<all>
<xsd:complexType name="PERSON">
<xsd:all>
 
<xsd:element name="firstName" type="xsd:string"/>
 
<xsd:element name="lastName" type="xsd:string"/>
 
<xsd:element name="ageInYears" type="xsd:int"/>
 
<xsd:element name="weightInLbs" type="xsd:float"/>
 
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:all>
</xsd:complexType> 

这意味着<element>的成员变量可以以任何顺序排列,每一个都是可选的。这和C中的struct类型不太一样。

  注意内建数据类型string, int, float。C的string也是XML的string,float也类似。但C中的long类型在XML中是int(上表中)。

  在WSDL文件中,像上面的complex类型可以在Types栏声明。例如,我可以用以下方式声明PERSON类型并用在Messages栏。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions … >
<types>
<schema targetNamespace="someNamespace" 
xmlns:typens
="someNamespace" >
<xsd:complexType name="PERSON">
<xsd:sequence>
 
<xsd:element name="firstName" type="xsd:string"/>
 
<xsd:element name="lastName" type="xsd:string"/>
 
<xsd:element name="ageInYears" type="xsd:int"/>
 
<xsd:element name="weightInLbs" type="xsd:float"/>
 
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</schema>
</types>

<message name="addPerson">
 
<part name="person" type="typens:PERSON"/>
</message>

<message name="addPersonResponse">
 
<part name="result" type="xsd:int"/>
</message>
</definitions> 

  上例中第一个消息由"adperson",并且有一个<part>,其类型为"PERSON"。PERSON类型是在Types栏声明的。

  如果我们使用完整的WSDL文件包含以上的部分,并以之初始化MSTK2 SoapClient,它将成功的解析该文件。当然,它不会去调用<addPerson>。这是因为SoapClient本身并不知道如何处理complex类型,它需要定制类型映射来处理complex类型。MSTK2文档中有包含定制类型映射的示例。

  还有另一种方法可以把<part>元素联系到类型声明。这就是使用元素。下例中我将Types栏中声明两个元素("Person"和"Gendr"),然后我将在"addPerson"<message>中使用元素属性来引用它们。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions … >
<types>
<schema targetNamespace="someNamespace" 
 xmlns:typens
="someNamespace" >
<element name="Person">
<xsd:complexType>
 
<xsd:sequence>
  
<xsd:element name="firstName" type="xsd:string"/>
  
<xsd:element name="lastName" type="xsd:string"/>
  
<xsd:element name="ageInYears" type="xsd:int"/>
  
<xsd:element name="weightInLbs" type="xsd:float"/>
  
<xsd:element name="heightInInches" type="xsd:float"/>
 
</xsd:sequence>
</xsd:complexType>
</element>
<element name="Gender">
<xsd:simpleType>
 
<xsd:restriction base="xsd:string">
  
<xsd:enumeration value="Male" />
  
<xsd:enumeration value="Female" />
 
</xsd:restriction>
</xsd:simpleType>
</element>
</schema>
</types>

<message name="addPerson">
 
<part name="who" element="typens:Person"/>
 
<part name="sex" element="typens:Gender"/>
</message>

<message name="addPersonResponse">
 
<part name="result" type="xsd:int"/>
</message>
</definitions> 

  Types栏中的Gender<element>里嵌入了枚举类型,其枚举值为"Male""Female"。然后我又在"addPerson"<message>中通过元素属性而不是类型属性来引用它。

  "元素属性"和"类型属性"在把某特定类型关联到<part>时有什么不同呢?使用元素属性,我们可以描述一个部分,它可以假定几个类型(就像变量一样),而是用类型属性我们就无法这样做。下例说明了这一点。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions … >
<types>
<schema targetNamespace="someNamespace" 
xmlns:typens
="someNamespace">
<xsd:complexType name="PERSON">
 
<xsd:sequence>
  
<xsd:element name="firstName" type="xsd:string"/>
  
<xsd:element name="lastName" type="xsd:string"/>
  
<xsd:element name="ageInYears" type="xsd:int"/>
  
<xsd:element name="weightInLbs" type="xsd:float"/>
  
<xsd:element name="heightInInches" type="xsd:float"/>
 
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="femalePerson">
<xsd:complexContent>
 
<xsd:extension base="typens:PERSON" >
 
<xsd:element name="favoriteLipstick" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="malePerson">
<xsd:complexContent>
<xsd:extension base="typens:PERSON" >
<xsd:element name="favoriteShavingLotion" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="maleOrFemalePerson">
<xsd:choice>
 
<xsd:element name="fArg" type="typens:femalePerson" >
 
<xsd:element name="mArg" type="typens:malePerson" />
</xsd:choice>
</xsd:complexType>
</schema>
</types>

<message name="addPerson">
 
<part name="person" type="typens:maleOrFemalePerson"/>
</message>

<message name="addPersonResponse">
 
<part name="result" type="xsd:int"/>
</message>
</definitions> 

  上例也告诉我们extension的派生。"femailPerson"和"malePerson"都是从"PERSON"派生出来的。它们各有一些额外的元素:"femalePerson"有"favoriteLipstick"元素,"malePerson"有"favoriteShavingLotion"元素。两派生类型都归入一个complex类型"maleOrFemalePerson",使用的是<choice>构造。最后,在"adperson"<message>中,新类型有"person"<part>引用。这样,参数或<part>就可以是"femalePerson"或"malePerson"了。

数组
  XSD提供<list>结构来声明一个数组,元素之间有空格界定。不过SOAP不是使用XSD来编码数组的,它定义了自己的数组类型--"SOAP-ENC: Array"。下列的例子揭示了从这一类型派生出一位整数数组的方法:

<xsd:complexType name="ArrayOfInt">
<xsd:complexContent>
 
<xsd:restriction base="soapenc:Array">
  
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:int[]"/>
 
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>

  新的complex类型从soapenc:array限制派生。然后又声明了complex类型的一个属性。引用"soapenc:arrayType"实际上是这样完成的:

<xsd:attribute name="arrayType" type="xsd:string"/>

wsdl:arrayType属性值决定了数组每个成员的类型。数组的成员也可以是Complex类型。:

<xsd:complexType name="ArrayOfPERSON">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" 
wsdl:arrayType
="typens:PERSON[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType> 

  WSDL要求数组的类型由"ArrayOf"和每个数组元素的类型串联而成。很显然,顾名思义,"ArrayOfPERSON"是PERSON结构的数组。下面我将使用ArrayOfPERSON来声明一个<message>,并加入不止一个PERSON:

<?xml version="1.0" encoding="UTF-8" ?>
<definitions … >
<types>
<schema targetNamespace="someNamespace" 
xmlns:typens
="someNamespace" >
<xsd:complexType name="PERSON">
 
<xsd:sequence>
  
<xsd:element name="firstName" type="xsd:string"/>
  
<xsd:element name="lastName" type="xsd:string"/>
  
<xsd:element name="ageInYears" type="xsd:int"/>
  
<xsd:element name="weightInLbs" type="xsd:float"/>
  
<xsd:element name="heightInInches" type="xsd:float"/>
 
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ArrayOfPERSON">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" 
wsdl:arrayType
="typens:PERSON[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</schema>
</types>

<message name="addPersons">
 
<part name="person" type="typens:ArrayOfPERSON"/>
</message>

<message name="addPersonResponse">
 
<part name="result" type="xsd:int"/>
</message>

</definitions>


 

posted on 2007-01-22 12:37 Java初心 阅读(1399) 评论(0)  编辑  收藏 所属分类: Web Service

只有注册用户登录后才能发表评论。


网站导航:
 

<2007年1月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(1)

随笔分类

随笔档案

文章分类

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜