[b]三、 wsdl语言[/b]
想象一下这样的场景:处于各种各样的理由,你已经决定允许其他人通过web来访问你的一组服务项目,也许是一个电子商务软件,也许是一个驱动某个网络设备的应用程序接口,根据客户的请求,你将向他们返回一个soap响应,一份xml文档或一幅图像。可你怎样才能让那些用户知道你提供这些服务呢?办法有两种,一是自己编写一个“发现”这些服务的机制,二是使用web服务描述语言(Web Services Description Language, [b]WSDL[/b])和一个[b]UDDI[/b]注册表。本篇只关注WSDL部分,它是一种用XML来描述网络服务的语言。
SOAP技术标准从没说过可以把不是SOAP的内容作为响应返回给对方,但它也没说不能这样做。许多人认为如果发送出去的是SOAP,那么返回来也应该是SOAP。WSDL澄清了这个错误概念,并提供了一种告诉对方”你预期的返回值是一种什么东西”的机制。它利用了三个现有的技术标准来实现这一机制,即[b]SOAP, HTTP(GET/POST)和MIME。[/b]
[b]3.1 wsdl简介[/b]
曾经有人说soap并不真需要什么接口描述语言。如果soap是交流纯内容的标准,那就需要一种语言来描述内容。soap消息确实带有某些[b]类型信息[/b],因此soap允许动态的决定类型。但不知道一个函数的函数名,参数的个数和各自类型,怎么可能去调用这个函数呢?没有wsdl,可以从必备文档中确定调用语法,或者检查消息。即便何种方法,都必须有人参与,这个过程可能会出错。而使用了wsdl,就可以通过这种跨平台和跨语言的方法使web service代理的产生自动化。
每个wsdl都定义了一项服务(service),而这项服务被定义为一组“端口(port)”。你可以把wsdl中的端口想象为URL地址,而不是TCP/IP中的数据通道。一个端口定义了一个”服务”项目的提供地点。一项服务可以有多个提供地点,但只能对一组事先安排好的“信息(message)”作出响应。“信息”是对通信数据的描述,每条信息由一组数据组成,这些数据必须定义为收发双方都知道的某种“类型(type)”。如果没有遇到其他方法更能说明问题的情况,则”类型”必须用XSD来定义。端口和信息结合在一起代表了一组“操作(operation)”,并定义了这个端口的“端口类型(portType)”。把一种协议和一种数据格式关联在一起就定义了一种可重复使用的“绑定(binding)”。把一个网络地址和一个绑定关联在一起就定义出了一个端口,而一组端口将定义出一项服务。综上所述,wsdl文档使用下面这些元素来定义一项网络服务:
[b]Type[/b]:数据类型定义的包容器。对类型的描述可以用xsd来完成。
[b]Message[/b]:定义通信中的数据。包括数据输入和输出。
[b]Operation[/b]:对某项服务所能完成的一个动作进行的抽象定义。
[b]Port[/b]:由一个绑定和一个网络地址所定义的一个端点。
[b]PortType[/b]:对一个或多个端口所支持的一组操作进行描述。
[b]Binding[/b]:为一个给定的端口类型安排协议和数据格式。
[b]Service[/b]:由一组相互关联的端口所构成的一个聚合。
在开始讨论这些术语之前,需要对wsdl技术标准所使用的命名空间前缀有一个了解。大家应该习惯使用这些前缀。wsdl技术标准所使用的名字空间前缀见下表(请参考wsdl 1.1文档的第1.2小节)。
[img]http://dl2.iteye.com/upload/attachment/0096/2278/c49aa0a0-41eb-3199-b77f-e9a622dbe98a.bmp[/img]
[b]3.2 定义一项web服务[/b]
任何web服务文档都是由一系列定义语句组成的。wsdl在她的xsd文档里定义了以下几种元素:
[b]definitions[/b]:所有xml文档需要有一个顶级文档作为树结构的[color=red]根[/color]。而wsdl技术标准选定的就是这个。
documentation:这个元素可以包含任意文本和元素。只要你就觉得有必要在文档里的某个位置加上一些额外的资料帮助其他人阅读和理解,就可以向这个元素注射一个注释文档进来。
message:对服务过程中锁发送和接收的数据进行的抽象定义。它可以由多个逻辑部分组成,每个部分必须与某个类型相关联。
portType:对给定端口上的可用操作集合进行的定义。
operation:给操作起个名字,并列出预期的输入和输出情况。每个元素还可以包含一个对该operation可能返回的出错数据进行描述的fault子元素。
input:对给定操作用作输入参数的元素进行描述。它还可以把input链接到一个指定的信息。
output:对给定操作作为输出参数返回的元素进行描述。它还可以把output链接到一个指定的信息。
fault:对可能返回的出错数据进行定义。
binding:为给定的portType所定义的操作和信息指定协议和数据格式。
service:用来把相关的端口组织在一起。
port:为一个给定的绑定分配地址。它通常以URN标识符的形式出现。一个典型的wsdl文档看起来使这个样子(其中的*,?,+就是普通的通配符含义,代表该元素出现的限制):
[img]http://dl2.iteye.com/upload/attachment/0096/2280/32ce8a12-5e4e-331e-83ac-aebd18c8cba1.bmp[/img]
[img]http://dl2.iteye.com/upload/attachment/0096/2282/66223bc0-f2f0-3434-94fd-96b5dabdd9dc.bmp[/img]
上面是wsdl文档可以包含的元素。wsdl文档可以分为[b]两部分[/b]:顶部分由[color=red]抽象定义[/color]组成,而底部分由[color=red]具体描述[/color]组成。抽象部分可以独立于平台和语言的方式定义soap消息,他们并不包含任何随机器或语言而变的元素。[b]抽象定义包括:Types,messages,portTypes;具体定义包括:Bindings,Services。[/b]
下图描述了他们之间的关系:
[img]http://dl2.iteye.com/upload/attachment/0096/2284/1b6961fe-e8dd-372e-ad93-b7c4f666b9e7.bmp[/img]
注意,文档之中可能只有一个Types栏,或根本没有。所有其他的栏可以只有零元素,单元素或多元素。wsdl的列表要求所有的栏固[color=red][b]定的顺序[/b][/color]出现:[b]import,types,message,portType,binding,service[/b].所有的抽象定义可以是单独存在于别的文件中,也可以从主文档中导入。
可以参考wsdl实例:
[url]http://www.blogjava.net/charles/archive/2008/12/15/246368.html[/url]
请注意上面这段代码中的“[b]import[/b]”元素。这个元素允许把一个命名空间和一个文档存储位置关联在一起。可以用来建立服务定义,还可以通过它重复使用其他文档里的部分或全部定义内容。下面是示例对比:
[b]一、 不带import元素的wsdl文档[/b]
[img]http://dl2.iteye.com/upload/attachment/0096/2286/6f4c5151-0a78-3b78-82d0-200e623500ef.bmp[/img]
随着服务项目的增加,一份像上面这样的文档可能会变得十分复杂,而import元素可以帮助我们把它分割成几个小文档。我们可以重复使用那些小文件对各种服务,类型和wsdl文档的其他组成部分进行定义。
我们看到上面的
和之间的类型定义无非是一些嵌入的xml schema,所以把他们单独提取出来作为一个文件时很简单的。
二、 带有import的wsdl
(1)下面先提取出http://example.com/stockquote/stockquote.xsd的内容
[img]http://dl2.iteye.com/upload/attachment/0096/2288/d35e9fd8-6811-30ad-b9f5-54da39d9c9f8.bmp[/img]
(2)接下来继续提取部分wsdl的内容称为http://example.com/stockquote/stockquote.wsdl
,其中还引入了上面的xsd。
[img]http://dl2.iteye.com/upload/attachment/0096/2292/10480185-724c-3824-bff1-15acefe191e5.bmp[/img]
(3)最后,用这两个小文件对这个portType上的绑定进行定义。下面是完整的http://example.com/stockquote/stockquoteservice.wsdl的内容。
[img]http://dl2.iteye.com/upload/attachment/0096/2290/a259aa69-c34d-3b3d-a5f7-2422c2330fd4.bmp[/img]
[img]http://dl2.iteye.com/upload/attachment/0096/2294/04c02c7e-dd09-32ab-b261-78b18ba0e8c9.bmp[/img]
[b]3.2.1 扩展元素和绑定[/b]
wsdl用绑定这个术语表示把协议和数据格式信息与一个信息,一个端口类型或者一个操作等关联在一起的动作。绑定中的某些元素表示某种特殊技术,wsdl技术标准称这类元素为扩展元素。这类元素一般用来对某种给定协议或者信息格式所特有的绑定信息进行定义。比如说,http扩展元素可以用来指定某个web站点里某个特定的web主页;而smtp扩展元素指定一个电子邮件地址。wsdl技术标准对扩展元素在wsdl定义里的出现位置做了规定,bool属性wsdl:required指明某个元素是否是必不可少的,它的缺省值是false(0)。
[b]3.2.2 对类型信息进行编码[/b]
wsdl把xsd作为它首选的类型定义系统。这使你能够对基于二进制数据的协议进行绑定。
[b]3.2.3 信息[/b]
信息(message)定义里包含一个或多个操作(operation)部分。message定义通过一个信息类型定义属性把每一个操作部分和一种类型关联在一起。因此,可以根据具体情况使用多个属性。为了配合xsd的使用,wsdl定义了名为[b]”element”和”type”[/b]的两个属性。wsdl中的element属性通过一个假名(Qname)对应一个xsd中的element; type属性通过一个Qname对应着一个xsd中的simpleType或complexType。举个例子,假设在大纲里有下面的定义:
[img]http://dl2.iteye.com/upload/attachment/0096/2296/df75be23-b967-34f6-bcd2-02b48eb3e0d8.bmp[/img]
如果把操作看做是[b]函数[/b],那么<message>元素就定义了web service函数的[b]参数[/b]。<message>元素中的每个<part>子元素都和某个参数相符,每个
都有名字和类型,就像参数有参数名和类型一样。输入参数在<message>元素中定义,与输出参数相隔离,输出参数有自己的<message>元素。兼作输入、输出的参数在输入输出的<message>元素中有它们相应的<part>元素。输出 <message>元素以"Response"结尾,对Java而言方法得返回值就对应一个输出的<message>。每个<part>元素都有名字和类 型属性,就像函数的参数有参数名和参数类型。
完整的各个部分的实例解释还可以参考:
[url]http://blog.csdn.net/simbi/article/details/6231151[/url]
[b]3.2.4 端口类型[/b]
wsdl中的端口(port)与基于套接字进行网络程序设计时说的端口含义是不同的。在wsdl世界里,一个端口类型(portType)实例指的是一组operation构成的特定组合。这个portType可能会被绑定到某个特定的TCP/IP端口,但这只不过是一种巧合而已。
portType定义的是一组operation,是一组抽象的操作。每个operation元素定义了调用PortType中所有方法的语法,每个operation最多可以由三个元素组成:wsdl:input,wsdl:output和wsdl:fault。有了这几个元素,就可以对operation和服务断点所支持的基本传输方式(单向,请求/响应等)作出定义。
某些基本传输方式可以带一个参数顺序(parameterOrder)属性。这个属性的作用是对绑定中使用的参数是否需要按一定顺序排列进行规定。operation定义对“是否需要在RPC类型的绑定中按一定顺序排列参数”是不做规定的。
如基于请求/响应的基本定义格式:
[img]http://dl2.iteye.com/upload/attachment/0096/2298/444da477-869f-3710-904e-216ccb1aa26b.bmp[/img]
[b]3.2.5 绑定[/b]
绑定(binding)语句的作用是为一个给定portType所定义的operation和message定义格式和协议细节,这个元素只能指定一种协议,不能出现任何地址信息。一个portType可以有一个或多个绑定定义。[b]属性可以包含input,output,fault元素,他们都对应于portType中的相同元素(通过name属性引用)[/b]。他们有下面的语法:
[img]http://dl2.iteye.com/upload/attachment/0096/2301/29afd483-2cdb-31e0-8eeb-31e7e59166e2.bmp[/img]
绑定定义中的name属性在整个wsdl文档的全部绑定中必须唯一;而它的type属性则必须指定绑定所确定的portType。input,output和fault信息的准确语法可以用扩展元素来定义。
[b]3.2.6 端口和服务[/b]
端口(port)的作用是为给绑定定义一个地址。服务(service)定义语句的作用是把彼此相关的多个端口归为一组,是一组port元素。
[img]http://dl2.iteye.com/upload/attachment/0096/2300/00bd522e-434c-3d8b-9289-9c92df2cbec0.bmp[/img]
在这种情况下,扩展元素将为port提供地址信息。一个port只能给出一个地址,而且不能用来给出地址信息以外的其他信息。service和port两个元素的名字属性在整个wsdl文档的范围内必须唯一。一个service里面的各个port必须尊守以下规则:
1.各个port之间不得进行通信,也就是不能有依赖关系。
2.一个service中允许存在其绑定映射到某个portType或映射到其他不同的地址上的port,而且这样的port可以有多个。
3.可以通过检查某项service的服务端口来确定该项service所提供的portType。用户可以利用这方面信息查明给定机器是否支持完成某项给定任务所需要的全部operation。
[b]3.3 soap绑定[/b]
wsdl通过一组扩展元素来定义如何使用soap接收和发送请求。这些元素提供以下几方面信息:
1.使用soap 1.1协议的port都有哪些
2.为一个soap端口指定一个地址。
3.为http信息头中的SOAPAction指定URI取值,这个值的作用是指明数据传输工作是否将在http协议上完成。
4.列出soap信息头的定义情况,这些Header元素可能出现在SOAP的封套中。
5.它提供一种用xsd对soap根进行定义的手段。
soap绑定用到的扩展元素的语法定义如下:
[img]http://dl2.iteye.com/upload/attachment/0096/2304/612c9638-4c48-3e97-bb02-27c1572ed6ab.bmp[/img]
[img]http://dl2.iteye.com/upload/attachment/0096/2306/2dfbe154-0b2e-3568-9edd-388be5153a3c.bmp[/img]
根据上面的语法,可以写出下面的wsdl文档来。
[img]http://dl2.iteye.com/upload/attachment/0096/2308/0af41e74-99cc-3830-9826-3696ea08fc10.bmp[/img]
[img]http://dl2.iteye.com/upload/attachment/0096/2310/6637554d-76cd-3ba0-969e-ecdae63ef7cb.bmp[/img]
接下来,我们对与soap绑定有关的各种元素的含义做一个介绍。
[b]3.3.1 soap:binding元素[/b]
这个元素指明整个绑定被绑定到soap上,包括soap的封套,信息头,信息体和出错信息等元素。[color=red][b]为了把一个binding绑定到soap格式,必须使用soap:binding元素。[/b][/color]这样,wsdl就跟soap关联上了。
soap:binding有两个可选的属性。transport属性指明这个binding对应哪一个soap传输机制。用来表明“这是一个符合soap技术标准的http绑定”的uri值是http://schemas.xmlsoap.org/soap/http。还可以使用其他uri来表明其他传输机制,比如smtp或纯tcp等。
[b]3.3.2 soap:operation元素[/b]
这个元素提供了关于整个operation集的信息。
(1)它有一个soapAction属性,作用是对预期的http SOAPAction信息头进行定义。在发出请求时不要修改这个值。只有在http上使用soap时才需要这个属性。
(2)还有一个style属性,它的作用是给出这个operation的类别:面向RPC还是面向文档。如果信息里包含有调用参数和返回值,就要使用”rpc”作为属性值;如果信息里包含的是文档,就要使用”document”作为属性的值。
[b]3.3.3 soap:body元素[/b]
soap:body元素对信息的各组成部分在soap信息体元素里的排列顺序进行定义。信息的组成部分既可以用直观的大纲来定义,也可以用抽象的类型定义来定义。
这个元素有一个可选的parts属性,作用是告诉wsdl文档的阅读者(程序)信息的哪些组成部分将出现在信息的soap信息里。如果省略这个属性,就表示信息的全体组成部分都将出现在soap信息体里。
soap:body元素必须永远包含一个use属性,这个属性的合法取值有两个,即”literal”和”encoded”。当设置为”encoded”时,信息中的各个部分将通过type属性指向某个抽象类型;当使用由encodingStyle指定的编码方案对信息进行编码时,那些抽象类型将生成一条确切的信息,在part定义里给出的name,type和namespace属性都成为编码方案的输入信息。namespace属性只有在抽象类型的type属性没有明确地对名字空间做出定义时才起作用。“由读取信息的那一方设置正确”只有在编码方案允许抽象类型的信息格式有所变化时才对信息起作用。soap技术标准中给出的编码方案也确实允许信息的格式有细微的变化。
如果想让信息“由写出信息的那一方设置正确”,就必须把use属性设置为”literal”。这意味着信息的每一个part都有直观的大纲。这种情况下,encodingStyle属性可以用来指明将使用哪一种编码方案来推导出实际完成编码操作的编码方案。
encodingStyle属性由一系列URI组成,他们彼此之间用一个空格隔开。
[b]3.3.4 soap:fault元素[/b]
soap:fault元素用来对将出现在soap的fault元素里的信息元素进行定义。它的name,use,encodingStyle和namespace属性与soap:body元素的同名属性作用相同。这个元素只能有一个part。
[b]3.3.5 soap:header元素[/b]
soap:header元素提供了对将出现在soap信息头元素里的信息部分进行定义的能力。不必把可能出现的每一条信息标题都列出来,因为wsdl文档里的中间环节和其他定义可能会把一些你不知道的信息标题加到最终的信息结果里去。信息标题的类型是通过element属性设置的。用于这个属性的大纲不得包括对soap信息头的actor和mustUnderstand属性的定义。
这个元素有一个可选的fault属性。
[b]3.3.6 soap:address元素[/b]
soap:address元素把一个URI和一个端口关联在一起。把一个端口绑定到soap上的时候,给出的必须是一个地址,其他任何东西都是错误的。
[b]3.4 GET/POST绑定和MIME绑定[/b]
为了描述web浏览器和web站点之间的交互操作,wsdl技术标准对http的get和post动作的绑定进行了规范。
wsdl还定义了一种通过MIME格式把抽象类型绑定到最终信息上的办法。
关于这两部分大家可以参考《soap:xml跨平台web service 开发技术》 5.4和5.5节。
[i]参考资料:
1.《soap:xml跨平台web service 开发技术》
2.《web service描述语言wsdl详解》
3.网络[/i]