知道美河 | 上传资料 | VIP申请 | 精品课程 | 资料搜索 | 问题反馈 | 会员手册 | 积分消费 | 积分充值 | 帐号保护
美河学习学习在线赞助VIP

美河学习在线(主站) eimhe.com

 找回密码
 建立账号
查看: 17011|回复: 19

[原创] SOAP较为详细文档(本人亲写)

[复制链接]
发表于 2007-8-15 15:52:17 | 显示全部楼层 |阅读模式
Web Services 这个词现在应该很火了吧。
因为工作原因 整理并写出了这份基础文档 希望高手们批评。。。。

还有我很早就申请了Eimhe号了 可惜没怎么来。。希望此论坛越办越好 并多点Web Services的东西 毕竟以后的主流Web 技术是基于SOA的了。。
 楼主| 发表于 2007-8-15 15:55:58 | 显示全部楼层
【謝幕-囄漇】整理编写  QQ:419761001


● 了解J2EE Web Services 概念
1: J2EE Web 服务是关于互操作性的,要掌握的最重要的事情就是互操作性了。
2        这些核心是由:WS-I(Web Services Interoperability Organization Web 服务互操作性组织)发布的 Basic Profile1.0(简称BP)
3        Bp提供了一套用于确定应用程序如何使用公共Web服务技术的规则。
4        Web 服务是一个符合Web Services互操作性组织的Basic Profile1.0的软件应用程序

★ 为什么要使用BP。
【必读】
        Web服务互操作组织WS-I是一个Web服务供应商的组织。这些供应商要给Web服务互操作性定义一个标准。WS-I的第一个可使用标准是Basic Profile 1.0(BP)。它详细的说明了如何在Web服务中一起使用4个主流的Web服务规范。BP定义了一些规则,来澄清XML,SOAP,WSDL和UDDI规范中的模糊部分,并定义了如何使用这些技术来注册,描述Web服务以及如何与Web服务通信。
        BP是必不可少的,因为各主要规范涉及的范围太广泛,不能对给定的互操作性给予说明。例如:WSDL是一个非常通用的技术,它允许用户描述各种类型的Web服务。遗憾的是,由于WSDL太通用了,所以在某些场合下难以准确的确定如何格式化要于WEB服务交换的SOAP消息。而BP通过准确的说明WSDL如何描述基于SOAP的web服务并通过限制WSDL的使用解决了互操作性问题。

●        SOAP
1 其实Web Services 的核心就是SOAP和WSDL,它们隐含于J2EE Web Services平台的通信层于部署层
2 什么是SOAP
SOAP最初是简单对象访问协议(即Simple Object Access Propotol)的缩写,但它只是个名称而已。SOAP1.1是J2EE Web Services使用的标准消息传递协议,而且通常是Web服务的事实标准。
SOAP的主要应用是应用程序与应用程序之间的通信(A2A),且主要应用于商务对商务(B2B)的通信以及企业应用集成(EAI)。B2B和A2A是同一个问题的2个方面,两者都要解决集成软件应用程序以及共享数据这样的问题。为了使B2B和EAI真正起作用,协议必须是与平台无关,具有灵活性并且基于标准且通用的技术。
        SOAP技术与早期的B2B和EAI技术,如CORBA和EDI(电子文档交换技术)不一样,SOAP能满足这些要求,而且应用广泛,并已得到大多软件企业的支持,如(W3C,WS-I,OASIS)。
        其实SOAP只不过是伴随其使用规则的另一个XML标记语言。SOAP有一个清晰的目标,即通过网络交换数据。它将重点放在了如何封装,编码XML数据以及如何定义用于传输和接收这些数据的规则上了。简单的说,SOAP就是一个网络应用协议。

5为什么SOAP会非常受欢迎
        SOAP定义了如何通过软件以独立于各种编程语言或平台的方式来构造消息,处理消息,从而使那些用于不用编程语言编写的程序之间具有互操作性,并能在不同的操作系统上运行。
        它通过XML命名空间使SOAP具有更强的扩展性。而且它基于XML为基础,并且SOAP它用于HTTP隧道的标准方法。HTTP隧道是将另外一个协议隐藏与HTTP消息中以穿越未收到阻止的防火墙的过程。防火墙通常允许HTTP通过端口80通行,但会禁止或限制使用其他协议或端口。
5        SOAP的基本结构
<?xml version=”1.0” encoding=”UTF-8” ?>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/envelope/ >
<soap:Body>
…………………
…………………………
………………….
  </soap:Body>
</soap:Envelope>

SOAP消息与传统邮递服务中使用的信封类似。就像纸信封内包含信件一样,SOAP消息包含了XML数据。

SOAP中根元素为Envelope元素。
Envelope元素可以包含余个可选的Header元素,同时必须包含一个Body元素。
如果用户使用了Header元素,那么该元素必须是Envelope元素的直接子元素。并在Body元素之前。

Header元素以一个或多个不同XML元素的形式包含消息方面的信息,其中每一个元素描述与消息关联的服务的某些方面或质量(服务质量是说-------)

Header元素可以包含各种XML元素,这些XML元素用于描述安全凭证,ID,路由指令,或在Body元素中处理数据时涉及的其他非常重要的消息方面的信息。可以这样说,如果我们希望为每一个SOAP消息附加一个唯一的标识符,用于测试和登陆。虽然唯一标识符不是SOAP协议本身的部分,但用户可以容易的向Herder元素添加标识符,如下例:
<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope
Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id “ >
  <soap:Header>
      <mi:message-id>2223322332233232:fffff</mi:message>
  </soap:Header>
<soap:Body>
   <!--                   -->
</soap:Body>
</soap:Envelope>

值得注意的是:message-id元素称为文件头,并且该元素是由其自己的命名空间标识的任意XML元素。
又如下面例子,带XML数字签名文件头的SOAP消息
: <?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#>

<soap:Header>
   <mi:message-id>111111111111111111111111111111111:-8000</mi:message>
   <sec:Signature>
      <ds:signature>
          …………………
        ……………….
       </ds:signature>
     </sec:Signature>
</soap:Header>
</soap:Envelope>

也就是说,可以在Header元素中放入任意数量的文件头,而且每个代码快均由合适的对应的函数来处理。。。。

6        SOAP命名空间
XML命名空间在SOAP里起着非常重要的作用。因为SOAP可以在Header和Body元素中包含若干不同的XML元素,为了避免冲突,必须使用唯一的命名空间来标识它们。
如下例:
  : <?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#>

<soap:Header>
   <mi:message-id>111111111111111111111111111111111:-8000</mi:message>
   <sec:Signature>
      <ds:signature>
       </ds:signature>
     </sec:Signature>
</soap:Header>

<soap:Body sec:id=”Body”>
  <po:purchaseOrder orderDate=”2003-09-22”
     Xmlns:po = http://www.Monson-Haefel.com/jwsbook/po
     Xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
   ……………………………………………
   </po:purchaseOrder>

</soap:Body>
</soap:Envelope>
正是因为SOAP使用了XML命名空间,所以使SOAP成为灵活且可扩展的协议。。命名空间完全限定了元素名称或属性名称。。

【注意】在Envelope元素声明的第一个命名空间定义了标准SOAP元素(即Envelop,Header,和Body元素)的命名空间。。。。如:

<soap:Envelope>
<Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope”>
</soap:Envelope>
此命名空间确定了所使用的SOAP1.1版本,SOAP消息必须使用Envelope元素的命名空间声明成如下标准的SOAP1.1信封命名空间:
<xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope”>
此规则保证了所有确认消息使用完全相同的命名空间和XML模式。。。
Header元素中的每个文件头应有自己的命名空间,这一点非常重要,因为命名空间有帮助SOAP应用程序指定文件头并单独处理它们

SOAP消息的所有本地元素都必须是命名空间限定的元素(就是SOAP1.1命名空间为前缀),因为SOAP1.1的XML模式将ElementFormDefault属性设置为了”qualified”,此外,Basic profile1.0要求所有由Body元素包含的应用程序专用的元素必须喂限定元素。
同样可以通过声明xsi:schemaLocation属性来进行有效性检验,但大多数情况下,SOAP堆栈会在设计时处理此问题,并不需要在SOAP消息中显示的声明xsi: schemaLocation。
【解释】所谓的SOAP堆栈使用于处理和传递SOAP消息的代码库。比如,J2EE1.4,Microsoft.NET都用自己的SOAP堆栈,即用于处理SOAP消息的自己的代码库。

【补充】有些SOAP堆栈要大量利用XML模式实例命名空间来说明元素的数据类型(比如:xsi:type=”xsd:float”),可是有些SOAP则不一样。当接收方需要类型化元素但发送方没有类型化元素时,这样的堆栈就会出问题。根据BP,只能用xsi:type属性来表示正在派生的XML类型替换它的基本数据,
对SOAP和应用程序专用的数据使用完全限定的名称会告诉SOAP接收方如何处理消息,以及要采用哪些XML模式来验证内容。。如:
①        文件头中的具体版本差异会影响接受方处理处理消息的方式,所以通过其命名空间指定的文件头版本可以使接收方切换处理模块,或当它不支持的时候拒绝消息。
②        合理的确定Body元素中包含的XML元素的类型可以使SOAP接收方用对应的代码模块处理这些元素,或者当它不支持指定的额命名空间时可能会拒绝消息。

【总结】
可以看出,SOAP消息可以包含许多不同的命名空间,从而使SOAP消息传递更具模块化,此模块化使得SOAP消息的不同部分能够独立于其他部分加以处理,并且能单独进行更改。
也就是说,我们可以不断更改SOAP Envelope或文件头的版本。但Body元素中应用程序专用内容的结构还是保持不变。
  【注意】 SOAP消息传递模式的模块化也允许处理SOAP消息的代码模块化
①        处理Envelope元素的代码独立于处理文件头的代码
②        而文件头独立于SOAP Body 元素中处理应用程序专用的数据代码。
③        模块化使用户能够用不同的代码库来处理SOAP消息的不同部分。
7        SOAP消息命名空间的特殊性
SOAP消息的命名空间目前为止都是在Envelope中,但并非一定可以这样做。
可以在Header元素或在文件头中声明这些命名空间。命名空间总是局部地确定范围,而且可以在任何级别加以声明,只要讨论的问题位于该范围内即可。 如下例:
                     在Header元素中声明命名空间
<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
<soap:Header
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#>
   <mi:message-id>111111111111111111111111111111111:-8000</mi:message>
   <sec:Signature>
      <ds:signature>
       </ds:signature>
     </sec:Signature>
</soap:Header>

<soap:Body sec:id=”Body”>
  <po:purchaseOrder orderDate=”2003-09-22”
   
   ……………………………………………
   </po:purchaseOrder>

</soap:Body>
</soap:Envelope>
                    在文件头中声明命名空间

<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
<soap:Header>

   <mi:message-id>111111111111111111111111111111111:-8000</mi:message>

   <sec:Signature>
      <ds:signature
                Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#
>
       </ds:signature>
     </sec:Signature>
</soap:Header>

<soap:Body sec:id=”Body”>
  <po:purchaseOrder orderDate=”2003-09-22”
   
   ……………………………………………
   </po:purchaseOrder>

</soap:Body>
</soap:Envelope>
这里在补充一下的是,虽然要求用前缀来限定Body元素中的应用程序专用的元素,但对包含在Header元素中的元素没有这样额要求。。


下表是WS-I Basic Profile 1.0中使用的命名空间前缀:

前缀        命名空间
Soap        “http://schemas.xmlsoap.org/soap/envelope
Xsi        “http://www.w3.org/2001/XMLSchema-instance
Xsd        “http://www.w3.org/2001/XMLSchema
Soapenc        “http://schemas.xmlsoap.org/soap/encoding/
Wsdl        “http://schemas.xmlsoap.org/wsdl/
Soapbind        “http://schemas.xmlsoap.org/wsdl/soap/
Wsi        “http://ws-i.org/schema/conformanceClaim/

SOAP头
首先要说明的是SOAP规范(制定者是Microsoft):
        1  SOAP信封:
                      请求,响应,标识所要应用程序功能点的消息结构。
2        SOAP编码方式:
序列化SOAP消息和这些消息中的应用程序定义类型的标准机制
3        SOAP RPC表示:
表示远程过程调用的机制

而SOAP头规范定义了一些规则,在消息路径中必须根据这些规则来处理文件头。而消息路径则是SOAP消息从初始发送方到最终接收方的路由。SOAP规则要指定哪些节点必须处理特殊的文件头,以及对文件头进行处理后应该做什么方面的事。
   
SOAP术语介绍:
SOAP与其他应用程序协议不同,SOAP并不显至于单个的消息传递范例,SOAP可以与多种消息传递系统(如同步,异步,RPC,单向等)一起使用,可以用非传统的方式组合它们。而SOAP是用于通过网络在SOAP应用程序间交换消息的协议。所以可以这样理解:
SOAP应用程序只不过是一个用于产生或处理SOAP消息的软件,发送SOAP消息的应用程序称为发送方。接收SOAP消息的应用程序称为接收方。而这些应用程序有时候同时具备这2个功能。
SOAP消息沿消息路径从发送方传递到接收方,所有的SOAP消息起始于初始发送方,并在最终接收方结束。客户这一术语有时指请求消息的初始发送方,而术语Web服务则指请求消息的最终接收方。
消息传递以及运用的概念
当SOAP消息沿消息路径传递时,其头文件可以由任意数量的SOAP中介体解释与处理。
   SOAP中介体既可以是接收方也可以使发送方,,它可以接收SOAP消息,处理一个或多个文件头,并将SOAP消息传送到另外一个SOAP应用程序,沿消息路径(初始发送方,中介体,最终接收方)的各应用程序也称为SOAP节点

我希望通过一个例子来解释消息路径中的节点如何处理文件头。我们假设有2个相对简单的文件头Message-id和Processed-by。文件头Message-id用于记录SOAP应用程序(即节点),这些SOAP应用程序要沿着从初始发送方到最终接收方这一传递路径处理SOAP消息。
与Message-id文件头类似,Processed-by文件头用于调试程序与记录。
下例中,SOAP消息在到达最终接收方之前要通过若干个中接替传递。我来说说中介体以等的概念吧:
  假设SOAP消息的初始发送方是一个客户节点,最终接收方是一个运输节点,那么中间会经过一些必要的途径,比如:销售节点,应付款节点,库存节点,最后到运输节点。
从初始发送放到最终接受方中间所经过的一系列的节点,我们称为中介体。它们也会传送SOAP消息到下一个中介体。
还要在强调一个概念的是:位于SOAP消息路径中的各个中介体不能修改SOAP Body元素中的应用程序专用内容,但可以处理SOAP头文件,而且经常这么做。
在下面的例子中,要求每个SOAP中介体向Processed-by 文件头添加一个Note元素,以标识其本身并确定其处理消息的时间。程序在五个应用程序均向Processed-by文件头添加Node元素之后的消息。
<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#
Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”>

<soap:Header>
   <proc:processed-by>

<!—下面出现了5个节点。分别向Processed-by添加Node元素之后的消息-->
        <node>
           <time-in-millis>10136123146231</time-in-millis>
           <identity>http://www.customer.com</identity>
        </node>

        <node>
           <time-in-millis>101345416231</time-in-millis>
           <identity>http://www.Monson-Haefel.com </identity>
                </node>
<node>
           <time-in-millis>1035345234231</time-in-millis>
           <identity> http://www.Monson-Haefel.com </identity>
        </node>
<node>
           <time-in-millis>156564346231</time-in-millis>
           <identity> http://www.Monson-Haefel.com </identity>
        </node>
<node>
           <time-in-millis>103423454</time-in-millis>
           <identity> http://www.Monson-Haefel.com </identity>
        </node>

</proc:processed-by>
</soap:Header>

<soap:Body>
   <!—Application –Specific data goes Here-->
</soap:Body>
</soap:Envelope>
此例,当处理文件头时,每个节点在将SOAP消息传递到下一个接收方之前进行从SOAP消息读文件头,对文件头进行处理,然后删除文件头这样的操作。但消息路径中的节点如何知道它要处理哪一个文件头呢?
  SOAP1.1应用程序用actor属性来识别用于处理指定文件头的节点。还用mustUnderstand属性表示处理头的节点是否能够识别文件头以及知道如何处理它。
Actor属性
Actor属性由SOAP1.1注释定义,它是SOAP的Envelope,Body,和Header元素的命名空间(即Http://schemas,xmlsoap.org/soap/envelop/)的一部分。
  我们可以使用Actor属性指定具体节点所完成的功能、

Actor属性使用一个URI标识节点处理对应的文件头时必须扮演的角色。当一个节点接收到一个SOAP消息时,它要分析每一个文件头,以确定哪些代码块是由该节点所支持的加色(我在这里说一下,其实Actor这个属性就可以看成一个人,他在这个社会中会扮演很多的角色,比如:父亲 儿子 丈夫 医生,同样,Actor也是如此)
Actor属性与XML命名空间组合在一起使用,以确定要用哪一个代码模块处理具体的文件头。因此,接收节点首先要根据文件头的命名空间确定它是否起Actor属性所指定的角色。
然后选择对应的代码模块来处理这些文件头。。。
   所以,接收节点必须承认由赋予文件头的Actor属性所指定的角色,同时还要承认与文件头对应的XML命名空间。。
一个节点可以由对一个消息进行操作的多个模块,所以它会由多个角色。 这样的话,我们可以使用若干个不同的角色来标识消息路径中的每一个节点。
如下例:()
                                1-1例
<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#
Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”>

<soap:Header>
   <mi:message-id soap:actor=”http://www.Monson-Haefel.com/logger”>
111111111111111111111111111111111:-8000
</mi:message>
   <sec:Signature>
      <ds:signature>
       </ds:signature>
     </sec:Signature>
</soap:Header>

<soap:Body sec:id=”Body”>
  <po:purchaseOrder orderDate=”2003-09-22”
  ……………………………………………
   </po:purchaseOrder>
</soap:Body>
</soap:Envelope>
此例子,在消息路径中,只有用了Actor属性值http://www.Monson-Haefel.com/logger标识了的节点才能处理Message-id文件头,其他节点会忽略该文件头。

【注意】除了像Http://www.Monson-Haefel.com/logger这样的客户URI之外,SOAP还为actor属性标识了2个标准角色,即Next和Ultimate receiver角色,这两个标准角色标识应该处理文件头的各节点,而且它们具有较好的自释性
                Next   和  Ultimate receiver 角色
Next角色表示消息路径中的下一个节点必须处理文件头。Next角色有一个指定的URI,即http://schemas.xmlsoap.org/soap/actor/next,此URI必须作为actor属性的值
Ultimate receiver角色表示只有消息的最终接受方才能处理文件头。协议并没有为此提供明显的URI。。
如果某一角色在文件头中没有actor属性,那么表示该角色是ultimate receiver
我们先来看一个例子: 如下1-2例:
1-2例
<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#
Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”>

<soap:Header>
   <mi:message-id soap:actor=”http://www.Monson-Haefel.com/logger”>
111111111111111111111111111111111:-8000
</mi:message>
<proc:processed-by
Soap:actor=”Http://schemas.xmlsoap.org/soap/actor/next”>
   <note>
      <time-in-millis>132132156231</time-in-millis>
      <identity>http://www.customer.com/</identity>
    <note>
         </proc:processed-by>
   <sec:Signature>
      <ds:signature>
       </ds:signature>
     </sec:Signature>
</soap:Header>

<soap:Body sec:id=”Body”>
  <po:purchaseOrder orderDate=”2003-09-22”
  ……………………………………………
   </po:purchaseOrder>
</soap:Body>
</soap:Envelope>
【重点】本例中,此消息路径的下一个接收方无论提供其他何种服务,都应该处理Processed-by文件头。 当节点处理文件头时,它必须从SOAP消息中删除该文件头。节点也可以向消息添加新的文件头。SOAP节点通常通过修改文件头来假装的删除它(在逻辑上与删除,修改,然后再将文件头添加回SOAP消息一样)。这一点小小的技巧使节点传播文件头时能够遵循SOAP规范,不会丢失数据。
比如 1-1例中,节点可以删除Message-id文件头,但用户并不希望删除1-2例子中的processed-by文件头,因为希望消息路径中的所有节点都能向他添加信息。,因此1-1例中节点要向processed-by文件头添加自己的数据,然后将SOAP消息传递消息路径中的下一个节点。让我们看看例子1-3吧,其实它已经删除了message-id文件头,并且修改了processed-by文件头
1-3 例子

<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#
Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”>

<soap:Header>
  
<proc:processed-by
Soap:actor=”Http://schemas.xmlsoap.org/soap/actor/next”>
   <note>
      <time-in-millis>132132156231</time-in-millis>
      <identity>http://www.customer.com/</identity>
    <note>

   <note>
      <time-in-millis>132132156231</time-in-millis>
      <identity>http://www.customer.com/</identity>
    <note>

         </proc:processed-by>
   <sec:Signature>
      <ds:signature>
       </ds:signature>
     </sec:Signature>
</soap:Header>

<soap:Body sec:id=”Body”>
  <po:purchaseOrder orderDate=”2003-09-22”
  ……………………………………………
   </po:purchaseOrder>
</soap:Body>
</soap:Envelope>
mustUnderstand属性
使用next标准角色类型会有一些后遗症。在许多情况下,用户可能并不知道确切的消息路径或不知道消息路径中的各节点的功能,即用户并不总能知道各节点是否正确地处理文件头。例如:processed-by文件头所指向的 next角色,表示它下一个节点应该处理该代码块,但如果下一个节点不能识别该文件头的话,就会出问题了。
  文件头必须用mustUnderstand属性来表示处理是否为强制性处理。mustUnderstand属性由SOAP1.1命名空间http://schemas.xmlsoap.org/soap/envelop定义,该属性的值可以是“1“0”分别代表真和假。该属性默认为“0”。
MustUnderstand属性名中的Understand表示节点必须能够根据其XML结构和命名空间识别文件头,并知道如何处理它。换句话说:【这个是我自己总结的,如果节点扮演了由文件头的actor属性表示的角色,但没有写代码来处理文件头,那么就不会识别该文件头】
如果用户添加了中间节点但没考虑指向它的所有可能的文件头,或没有考虑next角色,那么就会产生一个SOAP异常【SOAP异常下面才讲到】,并放弃处理该消息,也就不能给下一个节点提供消息了。
1-4例子

<?xml version = “1.0” encoding = “UTF-8” ?>
<soap:Envelope>
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope

Xmlns:mi=”http://www.Monson-Haefel.com/jwsbook/message-id

Xmlns:sec=http://schemas.xmlsoap.org/soap/security/2000-12

Xmlns:ds=http://w3c.org/2000/09/xmldsig#
Xmlns:proc=”http://www.Monson-Haefel.com/jwsbook/processed-by”>

<soap:Header>
  <proc:processed-by
Soap:actor=http://schemas.xmlsoap.org/soap/actor/next
Soap:mustUnderstand=”1”>
<note>
      <time-in-millis>132132156231</time-in-millis>
      <identity>http://www.customer.com/</identity>
<note>

<note>
      <time-in-millis>132132156231</time-in-millis>
      <identity>http://www.customer.com/</identity>
<note>
</proc:processed-by>


</soap:Header>

<soap:Body sec:id=”Body”>
  <po:purchaseOrder orderDate=”2003-09-22”
  ……………………………………………
   </po:purchaseOrder>
</soap:Body>
</soap:Envelope>
【补充】如果您要添加一个新的节点的话,那么该节点也要处理发送过来的SOAP消息。假设当添加的新的节点,编程人员忘记包含处理processed-by文件头的逻辑了,因此新的节点不能识别processed-by文件头,并且不知道如何处理它。又由于mustUnderstand属性设置为“1”,所以新的节点不得不抛弃SOAP消息,产生一个SOAP异常,并且返回给发送方。。。。。
如果SOAP接收方无法识别强制性文件头,那么会要求它用错误代码mustUnderstand产生一个错误(稍后介绍)
传递模式与SOAP消息
是否将错误传递回发送方取决于消息传递交换模式(MEP)是单项还是请求、响应模式。如果SOAP应用程序使用了请求、响应消息传递交互模式,则要求将SOAP错误消息传递回发送方。如果SOAP应用程序使用的是单向消息传递模式,那么则不需要返回传递错误消息。
如果MustUnderstand属性的值为0,那么SOAP指定的处理要求则不一样。如果一个节点要完成由非强制性文件头申明的角色,而且某一应用程序没能识别该文件头(没能识别出XMK结构或命名空间),那么它必须删除该文件头。而且,并获没有强迫它去进行处理,也没有迫使它抛弃消息。可以随意删除文件头并将消息沿消息路径传递到下一个节点。
【推荐】
接收方不应该拒绝消息,应为还没有处理(和删除)指向其它某个节点的文件头。换句话说,接收方不应该试图根据目前有哪些文件头来确定路径中位于前面的节点是否成功的处理了消息。此规则特别适用于最终接受方,最终接受方不应该拒绝消息,因为从来不回处理用于某些未知角色的文件头。
如果接收方分局并未指向的文件头的状态开始分析并且拒绝消息,则不可能在更改消息路径是不考虑这些更改对下游造成的连锁反应,由于要求各节点“考虑其自己的业务”,因此消息路径可以变化,而且是很动态的。添加一个新的中介体(或删除它)并不需要调整消息路径中的其他各节点。
WS-I 一致性文件头
虽然BP没有涉及到特殊类型的文件头,但它确实指定了一个表示SOAP晓得遵循BP的可选一致性文件头。如1-6例:
1-6
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope”>
<soap:Header>
  <wsi:Claim conformsTo=http://ws-i.org/profiles/basic/1.0
Xmlns:wsi=http://ws-i.org/schemas/conformanceClaim />
</soap:Header>
<soap:Body sec:id=”Body”>
………………………..
</soap:Body >
</soap:Envelope>

WS-I Basic Profile指出并不需要Claim文件头,它还规定“不能将消息中缺少一致性声明最为消息是否符合一个或多个配置文件的判断”
  SOAP消息可以为它遵循的每一个配置文件声明一个单独的Claim文件头。
Claim元素只能作为Header元素的直接子元素来声明,它不能出现在SOAP消息的其他任何部分。此外,Claim文件头总是可选的,因此它的MustUnderstand属性值不能为“1”,用户不能要求接收方处理Claim文件头
【关于文件头的进一步说明】
SOAP文件头是扩展SOAP协议的一个功能非常强大的措施。对开发人员和开发商来说,SOAP文件头作为元数据的一中结构,使用起来要比采用其他协议中的类似机制(比如CORBA  IIOP中的“服务上下文”)更加灵活和更为容易。
Message-id和processed-by文件头知识创建的自己的定制的文件头,各社团通常会定义一般用途的SOAP文件头。这些机构主要是考虑的是强调服务质量的文件头,比如安全性,事务,消息持续性以及路由。
BP并没有强调这些潜在标准,但WS-I最终将创建更为高级的配置文件,该文件能够将在OASIS,W3C,Microsoft,IBM,以及其他机构中使用的许多协议组合成一起。

第二章 SOAP体
【重要的概念】
虽然Header元素是可选元素,但所有SOAP消息必须包含一个Body元素。Body元素要包含应用程序专用的数据或者错误消息。应用程序专用的数据是用户希望与Web服务进行交换的信息,它可以是任意XML数据或是提供给过程调用的任意参数。不论是哪一个数据,Body元素应包含正在交换的应用程序数据。只有错误发生时才会使用错误消息。发现了问题(如处理错误或消息构造不合适)的接收节点会将问题传递回消息路径中位于该节点之前的发送方。SOAP消息可以携带应用程序专用的数据或错误,但不能通用时携带两者。
   无论Body元素包含的是应用程序专用的数据还是错误,大多数SOAP专家认为只有SOAP消息的最终接送方才应该处理Body元素的内容。消息路径中的中介点可以浏览Body元素,但不应该以任意方式改变内容,这一点与文件头不相同。
   沿消息路径的各中介体均能处理文件头,但只有最终接收方才应该更改Body元素内容,这以点非常重要。

【SOAP消息传递模式】
   第一:Document/Literal消息传递模式
     在Document/Literal消息传递模式中,SOAP的Body元素包含一个XML文档段,该文档段是以个良好的XML元素,它包含独立于SOAP消息的属于XML模式和命名空间的任意应用程序数据。
  第二:RPC/Literal消息传递模式
     RPC/Literal消息传递模式可以使用SOAP消息对过程调用或对带参数和返回值的方法调用建立模型。在RPC/Literal消息传递模式中,总是将Body元素的内容格式化为Struts。RPC请求消息包含调用的方法名称与输入参数。
RPC响应消息则包含了返回值和各种输出参数(或错误)。
在许多情况下,RPC/Literal消息传递模式用于以传统组建的形式提供Web服务。传统组件可以是Servlet,无状态会话Bean,javaRMI对象,CORBA对象或DCOM组件。这些组件不会显式改变XML数据,但它们均有带参数和返回值的方法。
比如2-1例子:
Package  com.jwsbook.soap;
Import java.rmi.RemoteException;

Public interface BookQipte extends java.rmi.Remote{
   //Get the whilesale price of a book
   Public float getBookPrice(String ISBN) throws RemoteException,InvalidISBNException;
}
此例有一个名为BookQuote且使用JAX-RPC(一个J2EE Web Services端点),而且是BookQuote的远程借口。
getBookPrice()方法以ISBN(International Standard Book Number,国际标准书号)形式声明了一个参数,该参数付给每一个唯一的字符。当用户用对应的ISBN激活该方法时,Web服务返回信息。
   此JAX-RPC服务端点可以使用RPC/Literal消息传递模式,Web服务使用了2个SOAP消息,即一个请求消息和一个应答消息。请求消息是从初始发送方向Web服务传递,并包含方法名getBookPrice和ISBN字符串参数。
  应答消息要传递回初始发送方,并以Float值的形式包含了信息。
下面2-2用于BookQuote Web服务的SOAP请求消息
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
                <mh:getBookPrice>
                        <isbn>03030201</isbn>
                </mh:getBookPrice>
         </soap:Body>
</soap:Envelope>

例子2-3对2-2给出了对应的响应消息

<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
                <mh:getBookPrice>
                        <result>24.99</ result>
                </mh:getBookPrice>
         </soap:Body>
</soap:Envelope>

RPC/Literal消息传递模式与Document/Literal消息传递不同。Document/Literal消息传递模式不会假设在消息的Body元素内所包含元素的类型与结构(但文档要遵循一些XML模式)。RPC/Literal消息传递模式要携带一些简单的参数。RPC样式的消息传递是分布式技术中最常用的方式,其中包括EJB,CORBA,DCOM等。
   所以,SOAP为RPC样式的消息传递定义了一个标准的XML格式,即RPC/Literal传递。
RPC/Literal消息传递模式指定了在SOAP消息中的Body元素中表示方法以及它们的参数的方式。
   重要的是要知道RPC/Literal和Document/Literal消息传递模式与使用像JAX-RPC这样的工具的开发人员的观点可能有所区别,因为JAX-RPC可以为RPC/Literal和Document/literal提供过程调用语义。
   有些人对RPC/Literal模式的有效性提出了疑问:当能够使用Document/Literal模式时,为什么还要使用RPC/Literal模式呢??毫无疑问,某些时候Document/Literal模式更为简单,而且使用Document/Literal模式还可以采用XML模式检验。这些只有等待BP来涉及了。
消息传递模式与消息传递交换模式
我们会很容易把RPC/Literal与Document/Literal模式与单向和请求/响应消息交换模式(MEP)弄混,但两者的概念截然不同。
   当我们说消息传递模式是RPC/Literal或Document/Literal模式时,通常描述的是SOAP消息的有效负载:即XML文档段或者是与远程过程调用对应的参数和返回值的XML表示。
与此相反,单向和请求/响应消息交换模式表示消息的流向,而不是消息的内容。
   单向消息传递是单方向传递,而请求/响应消息传递则是双向传递。

其他的信息传递模式
  我这里就不说其他的信息传递模式了,因为RPC/Literal和Document/Literal模式已经过时了,而且BP坚决反对使用这2种模式,而且它们会引起一切互操作性问题。感兴趣的朋友可以在网上搜索这2个模式的详细信息。


第三章SOAP错误
【概念】
SOAP错误消息是SOAP应用程序用于向消息路径中的“上游”节点报告错误的机制。
SOAP错误是由接收方(指消息的中介体或最终接收方)产生的。只有使用了请求/响应消息传递模式时,才要求接收方将SOAP错误传递回发送方。
  在单向消息传递模式中,接收方应该产生一个错误,并可能将其存储到某处,但不必试图将错误传递到发送方。
  要将SOAP错误返回到接收方的直接发送方。比如:如果消息路径中的第三个节点产生了一个错误,那么会将该错误消息传递回消息路径中的第二个节点,并且不会传递到其他对方。也可以这样说,除非初始发送方也是直接发送方,否则不将错误消息传递到初始发送方。
SOAP注释已经承认了SOAP错误处理的重要性,因此,BP提供了用于产生和处理SOAP错误消息的很多方案,下面我们来看具体实例吧。
【Fault元素】
Body元素中包含Fault元素的SOAP消息称为错误消息。错误消息与异常类似,即当错误发生时,就会产生错误消息。在请求/响应消息传递中要使用错误消息。当处理一个请求消息时,消息路径中的节点会产生错误消息。当错误发生时,接收节点会将错误消息传递回上游发送方,而不是期待的应答消息。不正确的消息格式,版本的不匹配,处理文件头时的错误以及与应用程序对应用的错误均会引起错误。
当产生错误消息时,SOAP消息的Body元素必须只能包含一个Fault元素,Fault元素本身又必须包含一个faultcode元素和一个faultstring元素,同时可以包含faultactor和detail元素(后2个可选),下面我们看看3-1例的SOAP错误消息的:
3-1
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:Client</faultcode>
                          <faultstring>
               The ISBN value contains invalid characters
</faultstring>
<faultactor>http://www.xxxxxxxxxxxxx.com</faultactor>

<detail>
        <mh:InvalidIsbnFaultDetail>
        <offending-value>198333330</offending-value>
        <conformance-rules>
                ………………………….
                ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
</conformance-rules>
</mh:InvalidIsbnFaultDetail >
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>
【说明】
Fault元素和它的子元素是SOAP命名空间的部分,就像SOAP的Envelope元素和Body元素的关系一样。
Fault元素可以是非限定元素,也就是说它们可以不使用SOAP前缀来限定Fault元素的子元素。
Fault元素除了可以包含faultcode,faultstring,faultactor,detail元素歪,禁止它包含其他任何直接元素。

【faultcode元素】
Faultcode元素可以使用四个标准SOAP错误代码中的任何一个来标识错误。这四个标准错误代码是:Client,Server,VersionMismatch和MustUnderstand。
   需要注意的是,虽然用户可以使用任意一个错误代码,但用户只能使用上面给出的四个错误代码。。。。
        错误代码的含义总是与代码(本地名称)和命名空间(前缀)相关。如(soap:Client).
如果用户使用某一个标准的SOAP错误代码,那么命名空间前缀必须映射到SOAP命名空间http://schemas.xmlsoap.org/soap/envelope/
1        Client错误:
Client错误代码表示传递SOAP消息的节点出现了错误。一般来说,如果由于消息或数据有错使接收方不能处理SOAP消息,那么会将其看成客户错误,即发送方错误。
如果消息不是格式良好的消息,或包含无效数据,或少了所期望的信息(比如专用的文
件头),接收节点会产生一个Client错误。
        如3-3例:
3-3
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:Client</faultcode>
                          <faultstring>
               The ISBN value contains invalid characters
</faultstring>
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>
当一个节点接收到带Client码的消息时,它不应该试图重新发送同样的消息,而应该采取措施来纠正问题或异常。
2        Server错误:
Server错误代码表示接收到SOAP消息的节点发生故障或不能处理SOAP消息。该错误
是由接收节点(中介体或最终接收方)引起的错误反射,而且没有通过SOAP消息本身指出任何问题。在这样的情况下,发送方可以假设SOAP消息为正确,且当暂停一段时间使接收方有时间进行恢复后,再重新传递它。
比如当接收节点无法连接到数据库之类的资源时,会发生一个Server错误,如3-4例:
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:Server</faultcode>
                          <faultstring>
              Database is unavailable.
</faultstring>
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>
3        VersionMismatch错误
当接收节点不能识别SOAP消息的Envelope元素的命名空间时,就会产生一个
VersionMismatch错误。例子,如果SOAP1.1节点接收到一个SOAP1.2消息,那么就会用VersionMismatch码产生一个错误。如下例3-5:
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:VersionMismatch</faultcode>
                          <faultstring>
             Message was not SOAP 1.1
</faultstring>
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>

VersionMismatch错误只赋给Envelope,Header,Body和Fault元素的命名空间,不适用于SOAP消息的其他部分,如文件头,XML文档版本或者Body元素中的专用元素。
4        MustUnderstand 错误
当节点接收到一个SOAP消息时,它必须分析Header元素,以确定哪些文件头指向了
该节点(如果有的话),如果文件头指向当前节点(用actor属性实现),并将MustUnderstand属性设为“1”,那么需要节点知道如何访问文件头。如果节点不能识别该文件头,那么它必须要使用MustUnderstand码产生一个错误,如3-6例:
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:MustUnderstand</faultcode>
                          <faultstring>
             Mandatory header block not understaood
</faultstring>
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>

5        非标准的SOAP错误代码
用户可以使用非标准的SOAP错误代码,这些错误代码是由其他机构规定的,并且属
于单独的命名空间。如3-7例:
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=http://www.Monson-Haefel.com/jwsbook/BookQuote
Xmlns:wsse=”http://schemas.xmlsoap.org/ws/2002/06/secext”>

  <soap:Body>
     <soap:Fault>
           <faultcode>wsse:InvalidSecurityToken </faultcode>
                          <faultstring>
             Mandatory header block not understaood
</faultstring>
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>

6        faultstring元素
faultstring元素是强制性元素,它提供人能够阅读的错误描述。虽然需要faultstring元素,
但用于描述错误的文本并不是太标准化。
        Faultstring元素可以使用特殊属性xml:lang表示文本消息使用的语言(可选行)。IETEF RFC1766(详细情况请参考关于IETF附录)定义了对应的有效代码集。比如可以用西班牙语文本产生一个Client错误,如3-8:
        <?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:Client</faultcode>
                          <faultstring xml:lang=”es”>
             El ISBN tiene Letras ……………..
</faultstring>
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>
如果没有特别说明,没有使用xml:lang属性的话,那么假设的语言为en(英语)。
7        faultactor元素
faultactor元素表示遇到错误并产生了错误(即错误代码)的节点。如果错误节点是一个
中介体,那么需要使用faultactor元素,但如果错误节点是最终接收方,则该元素可以有可以有无。例如:假如消息路径中的一个中间节点没能识别出强制性(Mustderstand=“1”)processed-by文件头,那么它会产生一个MustUnstandard错误。例子3-9节点必须用faultactor元素表示其本身。
        <?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:MustUnderstand</faultcode>
                          <faultstring>
             Mandatory header block not understaood
</faultstring>
<faultactor>
        http://www.Monson-Haefel.com/jwsbook/authenticator
</faultactor>
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>
Faultactor元素可以包含任何URI,但它通常是错误节点的Internet地址,或者是由actor属性所实用的URI(如果文件头是错误源的话)。
        Faultactor既可以标识产生错误的节点,也可以标识当产生错误时所扮演的角色
8        detail元素
如果Body元素内容产生了一个错误,则必须包含错误消息的detail元素,但如果当处
理文件头时发生了错误,则不能包含该元素。例子3-10的SOAP消息详细提供了在faultstring元素中报告的无效的ISBN方面的消息。
<?xml version=”1.0” encoding=”UTF-8”>
<soap:Envelope
Xmlns:soap=http://schemas.xmlsoap.org/soap/enevlope
Xmlns:mh=”http://www.Monson-Haefel.com/jwsbook/BookQuote”>
  <soap:Body>
     <soap:Fault>
           <faultcode>soap:Client</faultcode>
                          <faultstring>
               The ISBN value contains invalid characters
</faultstring>
<faultactor>http://www.xxxxxxxxxxxxx.com</faultactor>

<detail>
        <mh:InvalidIsbnFaultDetail>
        <offending-value>198333330</offending-value>
        <conformance-rules>
                ………………………….
                ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
</conformance-rules>
</mh:InvalidIsbnFaultDetail >
</detail>
.
     </soap:Fault>
  </soap:Body>
</soap:Envelope>
【注意】detail元素可以根据对应的XML模式包含任意数量的专用元素,这些元素可以是限定元素,也可以是非限定元素,此外,detail本身可以包含任意的限定属性,只要它们不属于SOAP1.1命名空间http://schemas.xmlsoap.org/soap/envelope/即可。
【重要】使用空detail元素是合法的,但如果在处理原始消息的Body元素的内容时发生错误的话,则不能省略detail元素。

评分

1

查看全部评分

 楼主| 发表于 2007-8-15 15:59:22 | 显示全部楼层
占一楼  方便补充

  还有如果你也是搞技术的 转帖请注明作者。。。这是基本道德
-----------------------------------------------------------------------------------------
因为要弄海波龙的报表工具文档了。。没时间自己写一个WSDL文档出来了。。
我给大家一个参考文档文档吧。。。非常抱歉
--------------------------------------------------------------------------------------------
为什么使用WSDL? http://zx/saasservice/MainOneService.asmx
  像Internet协议之类的标准有没有为权威所利用,或者人们这样看待它是因为顺之所获的好处远远超出了代价?曾经有许多试图建立的标准都流产了。有时候,那些还没有普遍使用的标准甚至由法令或政府规定强行推出:Ada语言就是一例。
  我相信正是跟随标准所带来的好处使它广泛接受。例如,对于铁路服务来说,真正重要的是,不同公司所铺设的铁路结合到一起,或者是来自好几个公司的产品协调的工作在一起。几家大的企业合力建立了SOAP标准。Web Service描述语言(WSDL)向这种Web Service的提供商和用户推出了方便的协调工作的方法,使我们能更容易的获得SOAP的种种好处。几家公司的铁道并在一起不算什么难事,他们所需遵循的只是两轨间的标准距离。对Web Service来说,这要复杂得多。我们必须先制定出指定接口的标准格式。
  曾经有人说SOAP并不真需要什么接口描述语言。如果SOAP是交流纯内容的标准,那就需要一种语言来描述内容。SOAP消息确实带有某些类型信息,因此SOAP允许动态的决定类型。但不知道一个函数的函数名、参数的个数和各自类型,怎么可能去调用这个函数呢?没有WSDL,我可以从必备文档中确定调用语法,或者检查消息。随便何种方法,都必须有人参与,这个过程可能会有错。而使用了WSDL,我就可以通过这种跨平台和跨语言的方法使Web Service代理的产生自动化。就像COM和CORBA的IDL文件,WSDL文件由客户和服务器约定。
  注意由于WSDL设计成可以绑定除SOAP以外的其他协议,这里我们主要关注WSDL在HTTP上和SOAP的关系。同样,由于SOAP目前主要用来调用远程的过程和函数,WSDL支持SOAP传输的文档规范。WSDL 1.1已经作为记录递交给W3C(见http://www.w3.org/TR/wsdl.html
  WSDL文档结构
  若要理解XML文档,将之看作块状图表非常有用。下图以XML的文档形式说明了WSDL的结构,它揭示了WSDL文档五个栏之间的关系。
  WSDL文档可以分为两部分。顶部分由抽象定义组成,而底部分则由具体描述组成。抽象部分以独立于平台和语言的方式定义SOAP消息,它们并不包含任何随机器或语言而变的元素。这就定义了一系列服务,截然不同的网站都可以实现。随网站而异的东西如序列化便归入底部分,因为它包含具体的定义。
  l 抽象定义
    Types
    独立与机器和语言的类型定义
    Messages
    包括函数参数(输入与输出分开)或文档描述
    PortTypes
    引用消息部分中消息定义来描述函数签名(操作名、输入参数、输出参数)
  2 具体定义
    Bindings
    PortTypes部分的每一操作在此绑定实现
    Services
    确定每一绑定的端口地址
  下面的图中,箭头连接符代表文档不同栏之间的关系。点和箭头代表了引用或使用关系。双箭头代表"修改"关系。3-D的箭头代表了包含关系。这样,各Messages栏使用Types栏的定义,PortTypes栏使用Messages栏的定义;Bindings栏引用了PortTypes栏,Services栏引用Bindings栏,PortTypes和Bindings栏包含了operation元素,而Services栏包含了port元素。PortTypes栏里的operation元素由Bindings栏里的operation元素进一步修改或描述。
  在此背景中,我将使用标准的XML术语来描述WSDL文档。Element是指XML的元素,而"attribute"指元素的属性。于是:
<element attribute="attribute-value">contents</element>

  内容也可能由一个或多个元素以递归的方式组成。根元素是所有元素之中最高级的元素。子元素总是从属于另一个元素,父元素。
  注意,文档之中可能只有一个Types栏,或根本没有。所有其他的栏可以只有零元素、单元素或是多元素。WSDL的列表要求所有的栏以固定的顺序出现:import, types, message, portType, binding, service。所有的抽象可以是单独存在于别的文件中,也可以从主文档中导入。


       图一:抽象定义和具体定义
WSDL文件示例
  让我们来研究一下WSDL文件,看看它的结构,以及如何工作。请注意这是一个非常简单的WSDL文档实例。我们的意图只是说明它最显著的特征。以下的内容中包括更加详细的讨论。
<?xml version="1.0" encoding="UTF-8" ?>
<definitions name="FooSample"
 targetNamespace="http://tempuri.org/wsdl/"
 xmlns:wsdlns="http://tempuri.org/wsdl/"
 xmlns:typens="http://tempuri.org/xsd"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:stk="http://schemas.microsoft.com/soap-toolkit/wsdl-extension"
 xmlns="http://schemas.xmlsoap.org/wsdl/">

<types>
<schema targetNamespace="http://tempuri.org/xsd"
  xmlns="http://www.w3.org/2001/XMLSchema"
  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  elementFormDefault="qualified" >
</schema>
</types>

<message name="Simple.foo">
 <part name="arg" type="xsd:int"/>
</message>

<message name="Simple.fooResponse">
 <part name="result" type="xsd:int"/>
</message>

<portType name="SimplePortType">
 <operation name="foo" parameterOrder="arg" >
  <input message="wsdlns:Simple.foo"/>
  <output message="wsdlns:Simple.fooResponse"/>
 </operation>
</portType>

<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>

<service name="FOOSAMPLEService">
 <port name="SimplePort" binding="wsdlns:SimpleBinding">
  <soap:address location="http://carlos:8080/FooSample/FooSample.asp"/>
 </port>
</service>
</definitions>

  以下是该实例文档的总述:稍后我将详细讨论每一部分的细节。
  第一行申明该文档是XML。尽管这并不是必需的,但它有助于XML解析器决定是否解析WSDL文件或只是报错。第二行是WSDL文档的根元素:<definitions>。一些属性附属于根元素,就像<schema>子元素对于<types>元素。
  <types>元素包含了Types栏。如果没有需要声明的数据类型,这栏可以缺省。在WSDL范例中,没有应用程序特定的types声明,但我仍然使用了Types栏,只是为了声明schema namespaces。
  <message>元素包含了Messages栏。如果我们把操作看作函数,<message>元素定义了那个函数的参数。<message>元素中的每个<part>子元素都和某个参数相符。输入参数在<message>元素中定义,与输出参数相隔离--输出参数有自己的<message>元素。兼作输入、输出的参数在输入输出的<message>元素中有它们相应的<part>元素。输出<message>元素以"Response"结尾,就像以前所用的"fooResponse"。每个<part>元素都有名字和类型属性,就像函数的参数有参数名和参数类型。
  用于交换文档时,WSDL允许使用<message>元素来描述交换的文档。
  <part>元素的类型可以是XSD基类型,也可以是SOAP定义类型(soapenc)、WSDL定义类型(wsdl)或是Types栏定义的类型。
  一个PortTypes栏中,可以有零个、单个或多个<portType>元素。由于抽象PortType定义可以放置在分开的文件中,在某个WSDL文件中没有<portType>元素是可能的。上面的例子里只是用了一个<portType>元素。而一个<portType>元素可在<operation>元素中定义一个或是多个操作。示例仅使用了一个名为"foo"的<operation>元素。这和某个函数名相同。<operation>元素可以有一个、两个、三个子元素:<input>, <output> 和<fault>元素。每个<input>和<output>元素中的消息都引用Message栏中的相关的<message>元素。这样,示例中的整个<portType>元素就和以下的C函数等效:
 
 int foo(int arg);

  这个例子足见XML和C相比要冗长的多。(包括<message>元素,XML在示例中共使用了12行代码来表达相同的单行函数声明。)
  Bindings栏可以有零个、一个或者多个<binding>元素。它的意图是制定每个<operation>通过网络调用和回应。Services栏同样可以有零个、一个、多个<service>元素。它还包含了<port>元素,每个<port>元素引用一个Bindings栏里的<binding>元素。Bindings和Services栏都包含WSDL文档。
Namespace
  <definitions>和子节点<schema>都是namespace属性:
<definitions name="FooSample"
 targetNamespace="http://tempuri.org/wsdl/"
 xmlns:wsdlns="http://tempuri.org/wsdl/"
 xmlns:typens="http://tempuri.org/xsd"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:stk="http://schemas.microsoft.com/soap-toolkit/wsdl-extension"
 xmlns="http://schemas.xmlsoap.org/wsdl/">

<types>
 <schema targetNamespace="http://tempuri.org/xsd"
  xmlns="http://www.w3.org/2001/XMLSchema"
  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  elementFormDefault="qualified" >
 </schema>
</types>

  每个namespace属性都声明了一个缩略语,用在文档中。例如"xmlns:xsd"就为 http://www.w3.org/2001/XMLSchema定义了一个缩略语(xsd)。这就允许对该namespace的引用只需简单的在名字前加上前缀就可以了,如:"xsd:int"中的"xsd"就是合法的类型名。普通范围规则可运用于缩略前缀。也就是说,前缀所定义的元素只在元素中有效。
  Namespace派什么用?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指派。
  targetNamespace属性声明了一个namespace,元素中所有的声明的名字都列于其内。在WSDL示例中,<definitions>的targetNamespace 是http://tempuri.org/wsdl。这意味着所有在WSDL文档中声明的名字都属于这个namespace。<schema>元素有自己的targetNamespace属性,其值为 http://tempuri.org/xsd ,在<schma>元素中定义的所有名字都属于这个namespace而不是main的target namespace。
  <schema>元素的以下这行声明了默认的namespace。Schema中所有有效的名字都属于这个namespace。
xmlns="http://www.w3.org/2001/XMLSchema"

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        描述
Soapenc        http://schemas.xmlsoap.org/soap/encoding        SOAP 1.1 encoding
Wsdl        http://schemas.xmlsoap.org/wsdl/soap        WSDL 1.1
Xsd         http://www.w3.org/2001/XMLSchema        XML Schema

  XSD基类型
  下表是直接从MSTK2文档中取出的,列举了MSTK2所支持的所有XSD基类型。它也告诉在客户端或服务器端的WSDL读取程序如何把XSD类型映射到在VB、C++和IDL中相应的类型。
XSD (Soap)类型        变量类型         VB        C++        IDL        Comments
anyURI        VT_BSTR        String        BSTR        BSTR         
base64Binary         VT_ARRAY | VT_UI1        Byte()        SAFEARRAY        SAFEARRAY(unsigned char)         
Boolean        VT_BOOL         Boolean         VARIANT_BOOL        VARIANT_BOOL         
Byte        VT_I2        Integer        short        short        转换时验证范围有效性
Date        VT_DATE        Date        DATE        DATE        时间设为 oo:oo:oo
DateTime        VT_DATE        Date        DATE        DATE         
Double        VT_R8        Double        double        double         
Duration        VT_BSTR        String        BSTR        BSTR        不转换和生效
ENTITIES        VT_BSTR        String        BSTR        BSTR        不转换和生效
ENTITY        VT_BSTR        String        BSTR        BSTR        不转换和生效
Float        VT_R4        Single        float        float         
GDay        VT_BSTR        String        BSTR        BSTR        不转换和生效
GMonth        VT_BSTR        String        BSTR        BSTR        不转换和生效
GMonthDay        VT_BSTR        String        BSTR        BSTR        不转换和生效
GYear        VT_BSTR        String        BSTR        BSTR        不转换和生效
GYearMonth        VT_BSTR        String        BSTR        BSTR        不转换和生效
ID        VT_BSTR        String        BSTR        BSTR        不转换和生效
IDREF        VT_BSTR        String        BSTR        BSTR        不转换和生效
IDREFS        VT_BSTR        String        BSTR        BSTR        不转换和生效
Int        VT_I4        Long        long        long         
Integer        VT_DECIMAL        Variant        DECIMAL        DECIMAL        转换时范围生效
Language        VT_BSTR        String        BSTR        BSTR        不转换和生效
Long        VT_DECIMAL        Variant        DECIMAL        DECIMAL        转换时范围生效
Name        VT_BSTR        String        BSTR        BSTR        不转换和生效
NCName        VT_BSTR        String        BSTR        BSTR        不转换和生效
negativeInteger        VT_DECIMAL        Variant        DECIMAL        DECIMAL        转换时范围生效
NMTOKEN        VT_BSTR        String        BSTR        BSTR        不转换和生效
NMTOKENS        VT_BSTR        String        BSTR        BSTR        不转换和生效
nonNegativeIntege        VT_DECIMAL        Variant        DECIMAL        DECIMAL        转换时范围生效
nonPositiveInteger        VT_DECIMAL        Variant        DECIMA        DECIMAL        转换时范围生效
normalizedString        VT_BSTR        String        BSTR        BSTR         
NOTATION        VT_BSTR        String        BSTR        BSTR        不转换和生效
Number        VT_DECIMAL        Variant        DECIMAL        DECIMAL         
positiveInteger        VT_DECIMAL        Variant        DECIMAL        DECIMAL        转换时范围生效
Qname        VT_BSTR        String        BSTR        BSTR        不转换和生效
Short        VT_I2        Integer        short        short         
String        VT_BSTR         String        BSTR        BSTR         
Time        VT_DATE        Date        DATE        DATE        日设为1899年12月30日
Token        VT_BSTR        String        BSTR        BSTR        不转换和生效
unsignedByte        VT_UI1        Byte        unsigned char        unsigned char         
UnsignedInt        VT_DECIMAL        Variant        DECIMAL        DECIMAL        转换时范围生效
unsignedLong        VT_DECIMAL        Variant        DECIMAL        DECIMAL        转换时范围生效
unsignedShort        VT_UI4        Long        Long        Long        转换时范围生效

  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>
<portType>和<operation>元素
  PortType定义了一些抽象的操作。PortType中的operation元素定义了调用PortType中所有方法的语法,每一个operation元素声明了方法的名称、参数(使用<message>元素)和各自的类型(<part>元素要在所有<message>中声明)。
  在一篇WSDL文档中可以有几个<PortType>元素,每一个都和一些相关操作放在一起,就和COM和一组操作的接口相似。
  在<operation>元素中,可能会有至多一个<input>元素,一个<output>元素,以及一个<fault>元素。三个元素各有一个名字和一个消息属性。
  <input>, <output>, <fault>元素属性的名字有何含义呢?它们可以用来区别两个同名操作(重载)。例如,看下面两个C函数:
void foo(int arg);
void foo(string arg);

  这种重载在WSDL中可以这样表示:
<?xml version="1.0" encoding="UTF-8" ?>
<definitions name="fooDescription"
 targetNamespace="http://tempuri.org/wsdl/"
 xmlns:wsdlns="http://tempuri.org/wsdl/"
 xmlns:typens="http://tempuri.org/xsd"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:stk="http://schemas.microsoft.com/soap-toolkit/wsdl-
 extension"
 xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<schema targetNamespace="http://tempuri.org/xsd"
 xmlns="http://www.w3.org/2001/XMLSchema"
 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 elementFormDefault="qualified" >
</schema>
</types>

<message name="foo1">
 <part name="arg" type="xsd:int"/>
</message>

<message name="foo2">
 <part name="arg" type="xsd:string"/>
</message>

<portType name="fooSamplePortType">
<operation name="foo" parameterOrder="arg " >
 <input name="foo1" message="wsdlns:foo1"/>
</operation>
<operation name="foo" parameterOrder="arg " >
 <input name="foo2" message="wsdlns:foo2"/>
</operation>
</portType>

<binding name="fooSampleBinding" type="wsdlns:fooSamplePortType">
<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/foo1"/>
 <input name="foo1">
  <soap:body use="encoded" namespace="http://tempuri.org/message/"
    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
 </input>
</operation>
<operation name="foo">
<soap:operation soapAction="http://tempuri.org/action/foo2"/>
<input name="foo2">
<soap:body use="encoded"
   namespace="http://tempuri.org/message/"
   encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
/>
</input>
</operation>
</binding>

<service name="FOOService">
<port name="fooSamplePort" binding="fooSampleBinding">
<soap:address
  location="http://carlos:8080/fooService/foo.asp"/>
</port>
</service>
</definitions>

  到目前为止,还没有一种SOAP的实现支持重载。这对基于JAVA的客户端十分重要,因为JAVA服务器使用的接口用到JAVA的重载特性。而对基于COM的客户端,就不那么重要,因为COM是不支持重载的。
<binding>和<operation>元素
  Binding栏是完整描述协议、序列化和编码的地方,Types, Messages和PortType栏处理抽象的数据内容,而Binding栏是处理数据传输的物理实现。Binding栏把前三部分的抽象定义具体化。
  把相关的数据制定和消息声明分开,这意味着同一类型服务的提供者可以把一系列的操作标准化。每个提供者可以提供定制的binding来互相区分。WSDL也有一个重要的结构,使抽象定义可以放在分离的文件中,而不是和Bindings和Services在一起,这样可在不同的服务提供者之间提供标准化的抽象定义,这很有帮助。例如,银行可以用WSDL文档来标准化一些银行的操作。每个银行仍然可以自由的订制下层的协议、串行优化,及编码。
  下面是重载的WSDL示例 的Binding栏,重复在此以便讨论:
<binding name="fooSampleBinding" type="wsdlns:fooSamplePortType">
<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/foo1"/>
  <input name="foo1">
   <soap:body use="encoded" namespace="http://tempuri.org/message/"
         encodingStyle=
         "http://schemas.xmlsoap.org/soap/encoding/" />
  </input>
 </operation>
<operation name="foo">
<soap:operation soapAction="http://tempuri.org/action/foo2"/>
 <input name="foo2">
  <soap:body use="encoded"
          namespace="http://tempuri.org/message/"
        encodingStyle=
          "http://schemas.xmlsoap.org/soap/encoding/" />
 </input>
</operation>
</binding>

  <binding>元素已经取了一个名字(本例中"fooSampleBinding"),这样就可以被Services栏的<port>元素引用了。它有一个"type"的属性引用<portType>,本例中就是"wsdlns:fooSamplePortType"。第二行是MSTK2的扩展元素<stk:binding>,它指定了preferredEncoding属性为"UTF-8"。
  <soap:binding>元素指定了所使用的风格("rpc")和传输方式。Transport属性应用了一个namespace,正是这个namespace指明使用HTTP SOAP协议。
  有两个同以"foo"命名的<operation>元素。唯一不同的是它们各自的<input>名字,分别为"foo1"和"foo2"。两个<operation>元素中的<soap:operation>元素有同样的"soapAction"属性,是URI。soapAction属性是SOAP特定的URI,它只是简单的使用于SOAP消息。所产生的SOAP消息有一个SOAPAction头,而URI也仅在<soap:operation>元素里才起作用。soapAction属性在HTTP的binding中是必需的,但在其他非HTTP binding中却不要提供。目前它的使用并不清楚,但它似乎有助于本例中的两个"foo"操作。SOAP 1.1指明soapAction用来确定消息的"意图"。似乎服务器可以在不解析整个消息的情况下就能使用这一属性来发送消息。实际上,它的使用多种多样。<soap:operation>元素也可以包含另一属性,即"style"属性,在有必要冲突<soap:binding>元素指定的风格时可以使用。
  <operation>属性可以包含<input>, <output> 和<fault>的元素,它们都对应于PortType栏中的相同元素。只有<input>元素在上例中提供。这三个元素中的每一个可有一个可选的"name"属性,在本例中,我们用这种方法来区分同名操作。在本例的<input>元素中有一个<soap:body>元素,它指定了哪些信息被写进SOAP消息的信息体中。该元素有以下属性:
  Use
   用于制定数据是"encoded"还是"literal"。"Literal"指结果SOAP消息包含以抽象定义(Types, Messages, 和PortTypes)指定格式存在的数据。"Encoded"指"encodingStyle"属性决定了编码方式。
  Namespace
   每个SOAP消息体可以有其自己的namespace来防止命名冲突。这一属性制定的URI在结果SOAP消息中逐字使用。
  EncodingStyle
   对SOAP编码,它应该有以下URI值:
"http://schemas.xmlsoap.org/soap/encoding"
文档风格实现
  在前几栏中,<soap:binding>元素有一个类型属性,设为"rpc"。此属性设为"document"时会改变传输时消息的串行化。不同于函数签名,现在的消息是文档传输的。在这类binding中,<message>元素定义文档格式,而不是函数签名。作为例子,考虑以下WSDL片段:
<definitions
xmlns:stns="(SchemaTNS)"
xmlns:wtns="(WsdlTNS)"
targetNamespace="(WsdlTNS)">

<schema targetNamespace="(SchemaTNS)"
elementFormDefault="qualified">
<element name="SimpleElement" type="xsd:int"/>
<element name="CompositElement" type="stns:CompositeType"/>
<complexType name="CompositeType">
<all>
 <element name='a' type="xsd:int"/>
 <element name='b' type="xsd:string"/>
</all>
</complexType>
</schema>

<message...>
 <part name='p1' type="stns:CompositeType"/>
 <part name='p2' type="xsd:int"/>
 <part name='p3' element="stns:SimpleElement"/>
 <part name='p4' element="stns:CompositeElement"/>
</message>

</definitions>

  schema有两个元素:SimpleElement和CompositeElement,还有一个类型声明(CompositeType)。唯一声明的<message>元素有四个部分:p1:Composite型;p2:int型;p3:SimpleElement型;p4:CompositeElement型。以下有一个表,对四种类型的use/type决定的binding作一比较:rpc/literal, document/literal, rpc/encoded, 以及document/encoded。表指明了每种binding的表现。
  <service>和<port>元素
  service是一套<port>元素。在一一对应形式下,每个<port>元素都和一个location关联。如果同一个<binding>有多个<port>元素与之关联,可以使用额外的URL地址作为替换。
一个WSDL文档中可以有多个<service>元素,而且多个<service>元素十分有用,其中之一就是可以根据目标URL来组织端口。这样,我就可以方便的使用另一个<service>来重定向我的股市查询申请。我的客户端程序仍然工作,因为这种根据协议归类的服务不随服务而变化。多个<service>元素的另一个作用是根据特定的协议划分端口。例如,我可以把所有的HTTP端口放在同一个<service>中,所有的SMTP端口放在另一个<service>里。我的客户可以搜索与它可以处理的协议相匹配的<service>。
<service name="FOOService">
<port name="fooSamplePort" binding="fooSampleBinding">
 <soap:address
   location="http://carlos:8080/fooService/foo.asp"/>
</port>
</service>

  在一个WSDL文档中,<service>的name属性用来区分不同的service。因为同一个service中可以有多个端口,它们也有"name"属性。
  总结
  本文中我描述了WSDL文档关于SOAP方面的最显著的特点。不过应该说明的是WSDL并不仅限于HTTP上的SOAP。WSDL用来描述HTTP-POST、HTTP-GET、SMTP及其他协议时非常清晰。使用了WSDL,SOAP更加容易处理了,无论是开发者还是使用者。我相信WSDL和SOAP一起将会开创网络应用程序世界的新时代。
  WSDL的namespace里有一系列的XML元素。下表概述了那些元素、它们的属性和内容。
元素        属性        内容(子元素)
<definitions>        name
targetNamespace
xmlns (other namespaces)         <types>
<message>
<portType>
<binding>
<service>
<types>        (none)        <xsd:schema>
<message>        Name        <part>
<portType>        Name         <operation>
<binding>        name
type        <operation>
<service>        name         <port>
<part>         name
type         (empty)
<operation>        name
parameterOrder        <input>
<output>
<fault>
<input>         name
message        (empty)
<output>        name
message        (empty)
<fault>         name
message         (empty)
<port>        name
binding        <soap:address>

  资源:
  1. WSDL 1.1     2. SOAP 1.1     3. XML Schema Primer    4. MS SOAP Toolkit Download Site
  5. A tool for translating IDL to WSDL
  6. Free Web Services resources including a WSDL to VB proxy generator
  7. PocketSOAP: SOAP related components, tools & source code

[ 本帖最后由 xt19833 于 2007-8-19 12:34 编辑 ]
 楼主| 发表于 2007-8-15 22:05:57 | 显示全部楼层
SOAP 可以看成一种传输协议。。
  如果要弄成视频 等于是把WEB SERVICES + 背后的知识一起讲了。。比如:SOAP与附件JAVA API(SAAJ),JAVA API FOR WSDL(JWSDL),
JAVA API FOR XML-based RPC(JAX-RPC) 等等 许多背景知识 太长了
这个文档只是个协议的入门。。 因为美河关于J2EE WEB SERVICES的很少。。所以我就把自己写的发上来了

[ 本帖最后由 xt19833 于 2007-8-15 22:10 编辑 ]
发表于 2007-8-17 08:59:55 | 显示全部楼层
 楼主| 发表于 2007-8-17 15:54:32 | 显示全部楼层
第二个计划老总喊写出WSDL的东西。。
  我会第一时间共享出来的。。

希望大家一起成长。。
发表于 2007-8-18 10:23:19 | 显示全部楼层
发表于 2007-8-18 22:52:17 | 显示全部楼层
 楼主| 发表于 2007-8-19 12:12:14 | 显示全部楼层
写错地方了。。重写 。。。

[ 本帖最后由 xt19833 于 2007-8-19 12:34 编辑 ]
发表于 2007-8-19 15:40:12 | 显示全部楼层
发表于 2007-8-24 15:36:37 | 显示全部楼层
谢谢楼主,讲的很好,本人收下了!!!我也认为美河这方面是一个欠缺!
发表于 2007-8-30 09:59:13 | 显示全部楼层
谢谢楼主,讲的很好,本人收下了!!!我也认为美河这方面是一个欠缺!
发表于 2007-8-31 09:51:58 | 显示全部楼层
发表于 2007-8-31 10:07:25 | 显示全部楼层
别说构思了  光打这么长的篇文章想着就挺苦的拉  顶
 楼主| 发表于 2007-9-2 10:04:19 | 显示全部楼层
反正也是公司要求写J2EE WEB Services 索性发表出来了 也没什么。。
  可惜我觉得美河似乎没多少人需要。。。
发表于 2007-9-4 10:05:58 | 显示全部楼层
 楼主| 发表于 2008-1-20 15:48:49 | 显示全部楼层
发表于 2009-1-20 22:40:58 | 显示全部楼层
发表于 2009-4-5 16:21:28 | 显示全部楼层
您需要登录后才可以回帖 登录 | 建立账号

本版积分规则

 
QQ在线咨询

QQ|小黑屋|手机版|Archiver|美河学习在线 ( 浙网备33020302000026号 )

GMT+8, 2025-6-14 02:27

Powered by Discuz!

© 2001-2025 eimhe.com.

快速回复 返回顶部 返回列表