WSDL语法
<什么是WSDL语言>
WSDL(网络服务描述语言,Web Services Description Language)是一门基于 XML 的语言,用于描述 Web Services 以及如何对它们进行访问,参阅http://www.w3school.com.cn/wsdl/index.asp。
➤WSDL文档可以分为两部分,顶部分由抽象定义组成,而底部分由具体描述组成。
➣抽象部分
抽象部分以独立于平台和语言的方式定义SOAP消息,它们并不包含任何随机器或语言而变的元素。这就定义了一系列服务,截然不同的应用都可以实现。
➣具体部分
具体部分,如数据的序列化则归入底部分,因为它包含具体的定义。在上述的文档元 素中,<types>、<message>、<portType>属于抽象定义 层,<binding>、<service>属于具体定义层。所有的抽象可以是单独存在于别的文件中,也可以从主文档中导入。
Type :数据类型定义的包容器。对类型的描述可以用xsd来完成。
Message :定义通信中的数据。包括数据输入和输出。
Operation :对某项服务所能完成的一个动作进行的抽象定义。
Port :由一个绑定和一个网络地址所定义的一个端点。
PortType :对一个或多个端口所支持的一组操作进行描述。
Binding :为一个给定的端口类型安排协议和数据格式。
Service :由一组相互关联的端口所构成的一个聚合。
<WDSL实例>
通过http://localhost:9000/helloWorld?wsdl可以查看到XML文件,这就是WSDL(WebService Definition Language),将该wsdl文件通过浏览器“将页面另存为”保存为helloWorld.wsdl。完整的 WSDL 语法见http://www.w3school.com.cn/wsdl/wsdl_syntax.asp
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="HelloWorld"
targetNamespace="http://server.cxf.webservice.web.apps.lucl.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://server.cxf.webservice.web.apps.lucl.com/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
>
<!-- 定义数据类型 -->
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://server.cxf.webservice.web.apps.lucl.com/"
elementFormDefault="unqualified"
targetNamespace="http://server.cxf.webservice.web.apps.lucl.com/"
version="1.0">
<xs:element name="sayHi" type="tns:sayHi" />
<xs:element name="sayHiResponse" type="tns:sayHiResponse" />
<xs:complexType name="sayHi">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHiResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<!-- 定义消息分组 -->
<wsdl:message name="sayHiResponse">
<wsdl:part element="tns:sayHiResponse" name="parameters" />
</wsdl:message>
<wsdl:message name="sayHi">
<wsdl:part element="tns:sayHi" name="parameters" />
</wsdl:message>
<!-- 定义port type -->
<wsdl:portType name="HelloWorldPortType">
<wsdl:operation name="sayHi">
<wsdl:input message="tns:sayHi" name="sayHi" />
<wsdl:output message="tns:sayHiResponse" name="sayHiResponse" />
<!-- 指定当 Web 服务设法响应客户机的请求时所发生的任何消息级异常 -->
<!--
<wsdl:fault name="" message=""></wsdl:fault>
-->
</wsdl:operation>
</wsdl:portType>
<!-- binding操作到特定协议,即关联portType到协议,这里为SOAP -->
<!-- W3C 推荐了三个 Web 服务的绑定:
HTTP 上的 SOAP(SOAP over HTTP)
HTTP GET/POST
SOAP/MIME
-->
<wsdl:binding name="HelloWorldSoapBinding" type="tns:HelloWorldPortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="sayHi">
<soap:operation soapAction="" style="document" />
<wsdl:input name="sayHi">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="sayHiResponse">
<soap:body use="literal" />
</wsdl:output>
<!-- 应用portType处的fault -->
<!--
<wsdl:fault name="">
<soap:fault name="" use="literal"/>
</wsdl:fault>
-->
</wsdl:operation>
</wsdl:binding>
<!--
描述binding的连接信息,根据绑定所实现的 portType 来处理请求。
对于 HTTP 上的 SOAP,这就是指向那个进程的 URL。
-->
<wsdl:service name="HelloWorld">
<wsdl:port binding="tns:HelloWorldSoapBinding" name="HelloWorldPort">
<soap:address location="http://localhost:9000/helloWorld" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
<wsdl:definition>
WSDL的根元素,主要属性为name和targetNamespace两个。
name为公开的Web服务接口,默认为实现类+Service,可通过WebService的serviceNmae指定;
targetNamespace指定目标名称空间,属性值同样被后面的xmlns:tns属性作为值,默认是使用接口实现类的包名的反序(http://server.cxf.webservice.web.apps.lucl.com/)。
<wsdl:types>
xs:schema是定义xml的属性信息,如命名空空间。
通过<xs:element... 和<xs:complexType...对元素进行说明,wsdl2java会根据element生成java类,而根据complexType创建类的属性。<xs:element...>
name为定义的方法sayHi以及元素sayHiResponse,
type属性是tns:+name属性对应的方法名。
sayHi是对方法的封装,sayHiResponse是对返回值的封装。
说明:可以使用WebMethod的operationName属性来自定义名称。
<xs:complexType...>
这个元素通过name属性关联到<xs:element...,它为element元素指定封装的具体内容,
通过子元素<xs:sequence...指定(参数名和参数类型)。
@WebService(name="HelloWorldPortType")
public interface HelloWorld {
@WebMethod(operationName="cusSayHi")
@WebResult(name="sayHiToUser") String sayHi(@WebParam(name="text") String text);
}
<wsdl:message>
这个元素将输入参数(方法参数)和响应结果(方法返回值)、受检查的异常信息包装为消息。
<wsdl:portType>
指定Web服务的端口类型(Web服务会被发布为EndPoint端点服务),它的name属性默认为接口名称(可以使用@WebService 注解的name 属性指定值,默认为实现类+Port)。
子元素<wsdl:operation …指定该端点服务包含了那些操作( 方法),input/output指定操作的输入输出(通过属性message 绑定到前面声明过的消息)。
<wsdl:operation… 的子元素>
<wsdl:input message="tns:sayHi" name="sayHi" />
<wsdl:output message="tns:sayHiResponse" name="sayHiResponse" />
<wsdl:binding>
将前面的端点服务绑定到SOAP协议,其中<soap:xxx... 的style、use分别可以使用SOAPBinding注解的style、use属性指定值。<wsdl:operation...>
指定公开的操作(方法)
<wsdl:service>
name 属性指定服务名称。
与根元素name值相同,可通过WebService的serviceName属性指定,默认为实现类+Service。
子元素<wsdl:port… 的name 属性指定port 名称,可通过WebService的属性portName指定,默认为实现类+Port。
子元素<soap:address … 的location 属性指定Web 服务的地址。
<深入理解WSDL>
曾经有人说soap并不真需要什么接口描述语言。如果soap是交流纯内容的标准,那就需要一种语言来描述内容。soap消息确实带有某些 类型信息 ,因此soap允许动态的决定类型。但不知道一个函数的函数名,参数的个数和各自类型,怎么可能去调用这个函数呢?没有wsdl,可以从必备文档中确定调用语法,或者检查消息。即便何种方法,都必须有人参与,这个过程可能会出错。而使用了wsdl,就可以通过这种跨平台和跨语言的方法使web service代理的产生自动化。
每个wsdl都定义了一项服务(service),而这项服务被定义为一组“端口(port)”。你可以把wsdl中的端口想象为URL地址,而不是TCP/IP中的数据通道。一个端口定义了一个”服务”项目的提供地点。一项服务可以有多个提供地点,但只能对一组事先安排好的“信息(message)”作出响应。“信息”是对通信数据的描述,每条信息由一组数据组成,这些数据必须定义为收发双方都知道的某种“类型(type)”。如果没有遇到其他方法更能说明问题的情况,则”类型”必须用XSD来定义。端口和信息结合在一起代表了一组“操作(operation)”,并定义了这个端口的“端口类型(portType)”。把一种协议和一种数据格式关联在一起就定义了一种可重复使用的“绑定(binding)”。把一个网络地址和一个绑定关联在一起就定义出了一个端口,而一组端口将定义出一项服务。综上所述,wsdl文档使用下面这些元素来定义一项网络服务:
Type :数据类型定义的包容器。对类型的描述可以用xsd来完成。
Message :定义通信中的数据。包括数据输入和输出。
Operation :对某项服务所能完成的一个动作进行的抽象定义。
Port :由一个绑定和一个网络地址所定义的一个端点。
PortType :对一个或多个端口所支持的一组操作进行描述。
Binding :为一个给定的端口类型安排协议和数据格式。
Service :由一组相互关联的端口所构成的一个聚合。
在开始讨论这些术语之前,需要对wsdl技术标准所使用的命名空间前缀有一个了解。大家应该习惯使用这些前缀。wsdl技术标准所使用的名字空间前缀见下表。
任何web服务文档都是由一系列定义语句组成的。wsdl在她的xsd文档里定义了以下几种元素:
➣definitions
所有xml文档需要有一个顶级文档作为树结构的 根 。而wsdl技术标准选定的就是这个。
➣documentation
这个元素可以包含任意文本和元素。只要你就觉得有必要在文档里的某个位置加上一些额外的资料帮助其他人阅读和理解,就可以向这个元素注射一个注释文档进来。
➣message
对服务过程中锁发送和接收的数据进行的抽象定义。它可以由多个逻辑部分组成,每个部分必须与某个类型相关联。
➣portType
对给定端口上的可用操作集合进行的定义。
➣operation
给操作起个名字,并列出预期的输入和输出情况。每个元素还可以包含一个对该operation可能返回的出错数据进行描述的fault子元素。
➣input
对给定操作用作输入参数的元素进行描述。它还可以把input链接到一个指定的信息。
➣output
对给定操作作为输出参数返回的元素进行描述。它还可以把output链接到一个指定的信息。
➣fault
对可能返回的出错数据进行定义。
➣binding
为给定的portType所定义的操作和信息指定协议和数据格式。
➣service
用来把相关的端口组织在一起。
➣port
为一个给定的绑定分配地址。它通常以URN标识符的形式出现。一个典型的wsdl文档看起来使这个样子(其中的*,?,+就是普通的通配符含义,代表该元素出现的限制):
<ignore_js_op style="color:rgb(0, 0, 0); font-family:微软雅黑, Helvetica, 'Hiragino Sans GB', 微软雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size:1rem; font-style:normal; font-weight:normal; text-align:start; text-indent:0px;"> 上面是wsdl文档可以包含的元素。wsdl文档可以分为 两部分 :顶部分由 抽象定义 组成,而底部分由 具体描述 组成。抽象部分可以独立于平台和语言的方式定义soap消息,他们并不包含任何随机器或语言而变的元素。 抽象定义包括:Types,messages,portTypes;具体定义包括:Bindings,Services。➣下图描述了他们之间的关系:
注意:文档之中可能只有一个Types栏,或根本没有。所有其他的栏可以只有零元素,单元素或多元素。wsdl的列表要求所有的栏固 定的顺序 出现: import,types,message,portType,binding,service .所有的抽象定义可以是单独存在于别的文件中,也可以从主文档中导入。
可以参考wsdl实例:
http://www.blogjava.net/charles/archive/2008/12/15/246368.html
注意:上面这段代码中的“ import ”元素。这个元素允许把一个命名空间和一个文档存储位置关联在一起。可以用来建立服务定义,还可以通过它重复使用其他文档里的部分或全部定义内容。下面是示例对比:
➤ 不带import元素的wsdl文档
随着服务项目的增加,一份像上面这样的文档可能会变得十分复杂,而import元素可以帮助我们把它分割成几个小文档。我们可以重复使用那些小文件对各种服务,类型和wsdl文档的其他组成部分进行定义。我们看到上面的和之间的类型定义无非是一些嵌入的xml schema,所以把他们单独提取出来作为一个文件时很简单的。
➤带有import的wsdl
➣下面先提取出http://example.com/stockquote/stockquote.xsd的内容
➣接下来继续提取部分wsdl的内容称为http://example.com/stockquote/stockquote.wsdl ,其中还引入了上面的xsd。➣最后,用这两个小文件对这个portType上的绑定进行定义
下面是完整的http://example.com/stockquote/stockquoteservice.wsdl的内容。
<ignore_js_op style="color:rgb(0, 0, 0); font-family:微软雅黑, Helvetica, 'Hiragino Sans GB', 微软雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size:1rem; font-style:normal; font-weight:normal; text-align:start; text-indent:0px;">
➤扩展元素和绑定
wsdl用绑定这个术语表示把协议和数据格式信息与一个信息,一个端口类型或者一个操作等关联在一起的动作。绑定中的某些元素表示某种特殊技术,wsdl技术标准称这类元素为扩展元素。这类元素一般用来对某种给定协议或者信息格式所特有的绑定信息进行定义。比如说,http扩展元素可以用来指定某个web站点里某个特定的web主页;而smtp扩展元素指定一个电子邮件地址。wsdl技术标准对扩展元素在wsdl定义里的出现位置做了规定,bool属性wsdl:required指明某个元素是否是必不可少的,它的缺省值是false(0)。
➤对类型信息进行编码
wsdl把xsd作为它首选的类型定义系统。这使你能够对基于二进制数据的协议进行绑定。
➣ 信息
信息(message)定义里包含一个或多个操作(operation)部分。message定义通过一个信息类型定义属性把每一个操作部分和一种类型关联在一起。因此,可以根据具体情况使用多个属性。为了配合xsd的使用,wsdl定义了名为 ”element”和”type” 的两个属性。wsdl中的element属性通过一个假名(Qname)对应一个xsd中的element; type属性通过一个Qname对应着一个xsd中的simpleType或complexType。举个例子,假设在大纲里有下面的定义:
如果把操作看做是 函数 ,那么<message>元素就定义了web service函数的 参数 。<message>元素中的每个<part>子元素都和某个参数相符,每个都有名字和类型,就像参数有参数名和类型一样。输入参数在<message>元素中定义,与输出参数相隔离,输出参数有自己的<message>元素。兼作输入、输出的参数在输入输出的<message>元素中有它们相应的<part>元素。输出 <message>元素以"Response"结尾,对Java而言方法得返回值就对应一个输出的<message>。每个<part>元素都有名字和类 型属性,就像函数的参数有参数名和参数类型。
完整的各个部分的实例解释还可以参考: http://blog.csdn.net/simbi/article/details/6231151
➤端口类型
wsdl中的端口(port)与基于套接字进行网络程序设计时说的端口含义是不同的。在wsdl世界里,一个端口类型(portType)实例指的是一组operation构成的特定组合。这个portType可能会被绑定到某个特定的TCP/IP端口,但这只不过是一种巧合而已。
portType定义的是一组operation,是一组抽象的操作。每个operation元素定义了调用PortType中所有方法的语法,每个operation最多可以由三个元素组成:wsdl:input,wsdl:output和wsdl:fault。有了这几个元素,就可以对operation和服务断点所支持的基本传输方式(单向,请求/响应等)作出定义。
某些基本传输方式可以带一个参数顺序(parameterOrder)属性。这个属性的作用是对绑定中使用的参数是否需要按一定顺序排列进行规定。operation定义对“是否需要在RPC类型的绑定中按一定顺序排列参数”是不做规定的。
如基于请求/响应的基本定义格式:
➤绑定
绑定(binding)语句的作用是为一个给定portType所定义的operation和message定义格式和协议细节,这个元素只能指定一种协议,不能出现任何地址信息。一个portType可以有一个或多个绑定定义。 属性可以包含input,output,fault元素,他们都对应于portType中的相同元素(通过name属性引用) 。他们有下面的语法:
绑定定义中的name属性在整个wsdl文档的全部绑定中必须唯一;而它的type属性则必须指定绑定所确定的portType。input,output和fault信息的准确语法可以用扩展元素来定义。
<ignore_js_op style="color:rgb(0, 0, 0); font-family:微软雅黑, Helvetica, 'Hiragino Sans GB', 微软雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size:1rem; font-style:normal; font-weight:normal; text-align:start; text-indent:0px;">
在这种情况下,扩展元素将为port提供地址信息。一个port只能给出一个地址,而且不能用来给出地址信息以外的其他信息。service和port两个元素的名字属性在整个wsdl文档的范围内必须唯一。一个service里面的各个port必须尊守以下规则:
➣各个port之间不得进行通信,也就是不能有依赖关系。
➣一个service中允许存在其绑定映射到某个portType或映射到其他不同的地址上的port,而且这样的port可以有多个。
➣可以通过检查某项service的服务端口来确定该项service所提供的portType。用户可以利用这方面信息查明给定机器是否支持完成某项给定任务所需要的全部operation。
➤soap绑定
wsdl通过一组扩展元素来定义如何使用soap接收和发送请求。这些元素提供以下几方面信息:
➣使用soap 1.1协议的port都有哪些
➣为一个soap端口指定一个地址。
➣为http信息头中的SOAPAction指定URI取值,这个值的作用是指明数据传输工作是否将在http协议上完成。
➣列出soap信息头的定义情况,这些Header元素可能出现在SOAP的封套中。
➣它提供一种用xsd对soap根进行定义的手段。
soap绑定用到的扩展元素的语法定义如下:
<ignore_js_op style="color:rgb(0, 0, 0); font-family:微软雅黑, Helvetica, 'Hiragino Sans GB', 微软雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size:1rem; font-style:normal; font-weight:normal; text-align:start; text-indent:0px;"> 根据上面的语法,可以写出下面的wsdl文档来。
<ignore_js_op style="color:rgb(0, 0, 0); font-family:微软雅黑, Helvetica, 'Hiragino Sans GB', 微软雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size:1rem; font-style:normal; font-weight:normal; text-align:start; text-indent:0px;">
接下来,我们对与soap绑定有关的各种元素的含义做一个介绍。
➤soap:binding元素
这个元素指明整个绑定被绑定到soap上,包括soap的封套,信息头,信息体和出错信息等元素。 为了把一个binding绑定到soap格式,必须使用soap:binding元素。 这样,wsdl就跟soap关联上了。
soap:binding有两个可选的属性。transport属性指明这个binding对应哪一个soap传输机制。用来表明“这是一个符合soap技术标准的http绑定”的uri值是http://schemas.xmlsoap.org/soap/http。还可以使用其他uri来表明其他传输机制,比如smtp或纯tcp等。
➤soap:operation元素
这个元素提供了关于整个operation集的信息。
➣它有一个soapAction属性,作用是对预期的http SOAPAction信息头进行定义。在发出请求时不要修改这个值。只有在http上使用soap时才需要这个属性。
➣还有一个style属性,它的作用是给出这个operation的类别:面向RPC还是面向文档。如果信息里包含有调用参数和返回值,就要使用”rpc”作为属性值;如果信息里包含的是文档,就要使用”document”作为属性的值。
➤soap:body元素
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组成,他们彼此之间用一个空格隔开。
➤soap:fault元素
soap:fault元素用来对将出现在soap的fault元素里的信息元素进行定义。它的name,use,encodingStyle和namespace属性与soap:body元素的同名属性作用相同。这个元素只能有一个part。
➤soap:header元素
soap:header元素提供了对将出现在soap信息头元素里的信息部分进行定义的能力。不必把可能出现的每一条信息标题都列出来,因为wsdl文档里的中间环节和其他定义可能会把一些你不知道的信息标题加到最终的信息结果里去。信息标题的类型是通过element属性设置的。用于这个属性的大纲不得包括对soap信息头的actor和mustUnderstand属性的定义。
这个元素有一个可选的fault属性。
➤soap:address元素
soap:address元素把一个URI和一个端口关联在一起。把一个端口绑定到soap上的时候,给出的必须是一个地址,其他任何东西都是错误的。
➤ GET/POST绑定和MIME绑定
为了描述web浏览器和web站点之间的交互操作,wsdl技术标准对http的get和post动作的绑定进行了规范。
wsdl还定义了一种通过MIME格式把抽象类型绑定到最终信息上的办法。
➣首先是了解消息头header和ProbeMatches中的内容,非常重要,可以参考这里http://www.w3.org/Submission/ws-addressing/ 最好详细的学习一下,里面的内容非常重要。
➣其次需要理解的是,其实当你看完ws-addressing后你会发现,骨架代码中的结构体和SOAP消息中的内容是一一对应的,例如:
结构体osap->header对应SOAP消息的<SOAP-ENV:Header></SOAP-ENV:Header>中的内容,包含在header里的内容当然会包含在SOAP的header内。例如:
结构体soap->header->wsa__RelatesTo对应的是<wsa:RelatesTo></wsa:RelatesTo>。
➣最后需要理解的是,在代码中的"__"双下划线一般对应xml中的命名空间的":",下划线前是命名空间,后是具体内容。
➣最后的最后是要详细的阅读ONVIF Core Specification
下图为响应OnvifTestTool的Probe命令的SOAP消息
结合上图再分析代码就亲切多了。在ONVIF Core Specification的7.3.2.2 Scopes 一节描述了onvif需要的Scopes,这个是需要在程序里填充,具体填充什么,文档里说的很明确:
注意点是在太多,随便漏掉一个都可能会导致搜不到设备,下图是非常重要的一个:
SOAP1.1和SOAP1.2所使用的SOAP-ENV是不同的,ONVIF使用的是SOAP1.1,如果soapcpp2产生的nsmap文件中的SOAP-ENV是SOAP1.2版本的话,那么OnvifTestTool是不会识别设备发出的SOAP消息的。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">