由于通信协议和消息格式在 web 技术圈子里已经达到了标准化,我们知道在通常的开发过程中,对于对象的interface一定具备相应的sdk描述文档,web服务也是一种对象,只不过 它是被部署在web上而已。很自然的,我们也完全需要有对web服务这个对象的界面的sdk描述文档。然而这两者又不尽相同,一来目前在web上的应用已 经完全接受了xml这个基本的标准,基本上所有新出台的技术都是基于xml标准的,二来web服务的目标是即时装配,松散耦合以及自动集成的,这意味着 sdk描述文档应当是具备被机器识别的能力的。
也就是说,对于使用标准化的消息格式/通信协议的web服务,它需要以某种结构化的方式(即xml)对web服务的调用 /通信加以描述,而且实现这一点也显得非常重要,这是web服务即时装配的基本保证。wsdl正是这样一种描述语言,wsdl 定义了一套基于 xml的语法,将web服务描述为能够进行消息交换的服务访问点的集合,从而满足了这种需求。wsdl 服务定义为分布式系统提供了可机器识别的sdk文档,并且可用于描述自动执行应用程序通信中所涉及的细节。
wsdl 文档将web服务定义为服务访问点或端口的集合。在 wsdl 中,由于服务访问点和消息的抽象定义已从具体的服务部署或数据格式绑定中分离出来,因此可以对抽象定义进行再次使用:消息,指对交换数据的抽象描述;而端 口类型,指操作的抽象集合。用于特定端口类型的具体协议和数据格式规范构成了可以再次使用的绑定。将web访问地址与可再次使用的绑定相关联,可以定义一 个端口,而端口的集合则定义为服务。因此,wsdl 文档在web服务的定义中使用下列元素:
types - 数据类型定义的容器,它使用某种类型系统(一般地使用xml schema中的类型系统)。
message - 通信消息的数据结构的抽象类型化定义。使用types所定义的类型来定义整个消息的数据结构。
operation - 对服务中所支持的操作的抽象描述,一般单个operation描述了一个访问入口的请求/响应消息对。
porttype - 对于某个访问入口点类型所支持的操作的抽象集合,这些操作可以由一个或多个服务访问点来支持。
binding - 特定端口类型的具体协议和数据格式规范的绑定。
port - 定义为协议/数据格式绑定与具体web访问地址组合的单个服务访问点。
service - 相关服务访问点的集合。
大家可以参考下图,来理解一下wsdl文档的结构组织:

其中,types是一个数据类型定义的容器,包含了所有在消息定义中需要的xml元素的类型定义,我将在今后的文章中结合xml schema来详细说明如何进行类型定义。
porttype具体定义了一种服务访问入口的类型,何谓访问入口的类型呢?就是传入/传出消息的模式及其格式。一个 porttype可以包含若干个operation,而一个operation则是指访问入口支持的一种类型的调用。在wsdl里面支持四种访问入口调用的模式:
one-way 此操作可接受消息,但不会返回响应。
request-response 此操作可接受一个请求并会返回一个响应
solicit-response 此操作可发送一个请求,并会等待一个响应。
notification 此操作可发送一条消息,但不会等待响应。
一个 one-way 操作的例子:
<message name="newtermvalues">
<part name="term" type="xs:string"/>
<part name="value" type="xs:string"/>
</message>
<porttype name="glossaryterms">
<operation name="setterm">
<input name="newterm" message="newtermvalues"/>
</operation>
</porttype >
在这个例子中,端口 "glossaryterms" 定义了一个名为 "setterm" 的 one-way 操作。
这个 "setterm" 操作可接受新术语表项目消息的输入,这些消息使用一条名为 "newtermvalues" 的消息,此消息带有输入参数 "term" 和 "value"。不过,没有为这个操作定义任何输出。伪代码相当于:
function setterm(string term, string value){
...
return null;
}
一个 request-response 操作的例子:
<message name="gettermrequest">
<message name="gettermresponse">
<operation name="getterm">
<input message="gettermrequest"/>
<output message="gettermresponse"/>
</operation>
</porttype>
在这个例子中,<porttype> 元素把 "glossaryterms" 定义为某个端口的名称,把 "getterm" 定义为某个操作的名称。
操作 "getterm" 拥有一个名为 "gettermrequest" 的输入消息,以及一个名为 "gettermresponse" 的输出消息。
<message> 元素可定义每个消息的部件,以及相关联的数据类型。
对比传统的编程,glossaryterms 是一个函数库,而 "getterm" 是带有输入参数 "gettermrequest" 和返回参数 gettermresponse 的一个函数。
namespace的作用是要避免命名冲突。如果我建立一项web service,其中的wsdl文件包含一个名为"foo"的元素,而你想要使用我的服务与另一项服务连接作为补充,这样的话另一项服务的wsdl文件就 不能包含名为"foo"的元素。两个服务器程序只有在它们在两个事例中表示完全相同的东西时,才可以取相同的名字。如果有了表示区别的 namespace,我的网络服务里的"foo"就可以表示完全不同于另一个网络服务里"foo"的含义。在你的客户端里,你只要加以限制就可以引用我 的"foo"。
见下例:http://www.infotects.com/fooservice#foo 就是完全限制的名字,相当于"carlos:foo",如果我声明了carlos作为http://www.infotects.com /fooservice的快捷方式。请注意namespace中的url是用来确定它们的唯一性的,同时也便于定位。url所指向的地方不必是实际存在的 网络地址,也可以使用guid来代替或补充url。例如,guid"335db901-d44a-11d4-a96e-0080ad76435d"就是一 个合法的namespace指派。
<?xml version = "1.0" encoding = "utf-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<service name="helloservice" targetnamespace="urn:star" typenamespace="urn:star" packagename="helloservice">
<interface name="helloservice.helloif"/>
</service>
</configuration>
name =”helloservice” 服务名称:helloservice
targetnamespace=”urn:star” wsdl名字空间:urn:star
<interface name=”helloservice.helloif”/> 服务的端点(endpoint)接口:helloservice.helloif
uri、url和urn是识别、定位和命名互联网上的资源的标准途径; url,urn是uri的子集.
uri不能定位或读取/写入资源。这是统一的资源定位器(url)的任务。url是一种uri,但是它的大纲组件是已知的网络协议(简称协议),并且它把uri组件与某种协议处理程序(一种资源定位器和根据协议建立的约束规则与资源通讯的读/写机制)。
uri一般不能为资源提供持久不便的名称。这是统一的资源命名(urn)的任务。urn也是一种uri,但是全球唯一的、持久不便的,即使资源不在存在或不再使用。
web上地址的基本形式是uri,它代表统一资源标识符。有两种形式:
url:目前uri的最普遍形式就是无处不在的url或统一资源定位器。
urn:url的一种更新形式,统一资源名称(urn, uniform resource name)不依赖于位置 ,并且有可能减少失效连接的个数。但是其流行还需假以时日,因为它需要更精密软件的支持。
体系中的uri、url和urn是彼此关联的。uri的范畴位于体系的顶层,url和urn的范畴位于体系的底层。这种排列显示url和urn都是uri的子范畴, uri表示的是统一的资源标识,它是以某种统一的(标准化的)方式标识资源的简单字符串。
按照 uri 标准,上面的第一个例子 —— http://www.cisco.com/en/us/partners/index.html —— 实际上是一个 uri,并且它由以下几个组成部分:
方案名 (<code>http</code> )
域名 (<code>www.cisco.com</code> )
路径 (<code>/en/us/partners/index.html</code> )
其中的一些例子有:
<code>mailto:mbox@domain</code>
<code>ftp://host/file</code>
<code>http://domain/path</code>
<span><definitions name="travelapprove"
targetnamespace="http://eclipse.org/bpel/travel"
xmlns:tns="http://eclipse.org/bpel/travel"
xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
></span>
这句话也定义一个"travelapprove" 属于http://eclipse.org/bpel/travel命名空间!
<porttype name="stockquoteporttype">
<operation name="getlasttradeprice">
<input message="tns:getlasttradepriceinput"/>
<output message="tns:getlasttradepriceoutput"/>
</operation>
</porttype>
这部分定义了服务访问点的调用模式的类型,表明stockquoteservice的某个入口类型是请求/响应模式,请求消息是getlasttradepriceinput,而响应消息是getlasttradepriceoutput。
<binding name="stockquotesoapbinding" type="tns:stockquoteporttype">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getlasttradeprice">
<soap:operation soapaction="http://example.com/getlasttradeprice"/>
<input>
<soap:body use="literal" namespace="http://example.com/stockquote.xsd"
encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
</output>
</soap:operation>
</operation>
</soap:binding>
</binding>
这部分将服务访问点的抽象定义与soap http绑定,描述如何通过soap/http来访问按照前面描述的访问入口点类型部署的访问入口。其中规定了在具体soap调用时,应当使用的 soapaction是"http://example.com/getlasttradeprice",而请求/响应消息的编码风格都应当采用soap 规范默认定义的编码风格"http://schemas.xmlsoap.org/soap/encoding/"。
<service name="stockquoteservice">
<documentation>股票查询服务</documentation>
<port name="stockquoteport" binding="tns:stockquotebinding">
<soap:address location="http://example.com/stockquote"/>
</port>
</service>
</definitions>
这部分是具体的web服务的定义,在这个名为stockquoteservice的web服务中,提供了一个服务访问入口,访问地址是"http://example.com/stockquote",使用的消息模式是由前面的binding所定义的。