级别: 初级 Russell Butek, 软件工程师, IBM
2003 年 11 月 01 日 WSDL 绑定样式可以是 RPC 样式或文档样式。用法可以是编码的,也可以是文字的。您如何决定使用哪一种样式/用法的组合呢?本文将帮助您解决这个问题。 Web 服务是通过 WSDL 文档来描述的。WSDL 绑定描述了如何把服务绑定到消息传递协议(特别是 SOAP 消息传递协议)。WSDL SOAP 绑定可以是 RPC 样式的绑定,也可以是文档样式的绑定。同样,SOAP 绑定可以有编码的用法,也可以有文字的用法。这给我们提供了四种样式/用法模型: - RPC/编码
- RPC/文字
- 文档/编码
- 文档/文字
除了这些样式之外,还有一种样式也很常见,它称为文档/文字包装的样式,算上这一种,在创建 WSDL 文件时您就有了五种绑定样式可以从中选择。您应该选择哪一种呢? 对于本文的讨论,让我们从 清单1中的 Java 方法开始,并且对其应用 JAX-RPC Java-to-WSDL 规则(请参见 参考资料)。 清单 1. Java 方法 public void myMethod(int x); | RPC/编码 采用 清单1中的方法并且使用您喜欢的 Java-to-WSDL 工具来运行它,指定您想让它生成 RPC/编码的 WSDL。您最后应该得到如 清单2所示的 WSDL 片断。 清单 2. 用于 myMethod 的 RPC/编码的 WSDL <message name="myMethodRequest"> <part name="x" type="xsd:int"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's RPC/encoded. --> | 现在用“5”作为参数 x 的值来调用此方法。我们将发送一个与 清单3类似的 SOAP 消息。 清单 3. 用于 myMethod 的 RPC/编码的 SOAP 消息 <soap:envelope> <soap:body> <myMethod> <x xsi:type="xsd:int">5</x> </myMethod> </soap:body> </soap:envelope> | | 关于前缀和名称空间的注意事项 为了简单起见,在本文的大部分 XML 示例中,我省略了名称空间和前缀。不过,我还是使用了少数前缀,您可以假定它们是用下列名称空间进行定义的。 - xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
| | 对于这个 RPC/编码的示例中的 WSDL 和 SOAP 消息,有许多需要注意的事项: 优点
- WSDL 基本达到了尽可能地简单易懂的要求。
- 操作名出现在消息中,这样接收者就可以很轻松地把消息发送到方法的实现。
缺点
- 类型编码信息(比如
xsi:type="xsd:int" )通常就是降低吞吐量性能的开销。 - 您不能简单地检验此消息的有效性,因为只有
<x xsi:type="xsd:int">5</x> 行包含在 Schema 中定义的内容;其余的 soap:body 内容都来自 WSDL 定义。 有没有一种方法能够保留这些优点而消除其中的缺点呢?或许有。让我们来看一看 RPC/文字的样式。
RPC/文字 用于我们的方法的 RPC/文字的 WSDL 看起来与 RPC/编码的 WSDL 几乎一样(请参见 清单4)。只是绑定的用法由 编码改为 文字。仅此而已。 清单 4. 用于 myMethod 的 RPC/文字的 WSDL <message name="myMethodRequest"> <part name="x" type="xsd:int"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's RPC/ literal. --> | RPC/文字的 SOAP 消息又是怎样的呢(请参见 清单 5)?这里的更改要多一点。去掉了类型编码。 清单 5. 用于 myMethod 的 RPC/文字的 SOAP 消息 <soap:envelope> <soap:body> <myMethod> <x>5</x> </myMethod> </soap:body> </soap:envelope> | 下面是这种方法的优点和缺点: 优点
- WSDL 还是基本达到了尽可能地简单易懂的要求。
- 操作名仍然出现在消息中。
- 去掉了类型编码。
缺点
- 您仍然不能简单地检验此消息的有效性,因为只有
<x xsi:type="xsd:int">5</x> 行包含在 Schema 中定义的内容;其余的 soap:body 内容都来自 WSDL 定义。 文档样式如何呢?它们能够帮助克服这些困难吗?
文档 我不知道有谁懂得这种方法的真正含义。我也不知道这种方法的任何实现。它将可能从 WSDL 的后续版本中消失。所以我们还是讨论别的吧。
文档 文档/文字的 WSDL 对 RPC/文字的 WSDL 作了一些更改。它们之间的不同之处显示在 清单6中。 清单6. 用于 myMethod 的文档/文字的 WSDL <types> <schema> <element name="xElement" type="xsd:int"/> </schema> </types> <message name="myMethodRequest"> <part name="x" element="xElement"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's document/literal. --> | 而现在的 SOAP 应该如 清单 7所示: 清单7. 用于 myMethod 的文档/文字的 SOAP 消息 <soap:envelope> <soap:body> <xElement>5</xElement> </soap:body> </soap:envelope> | | 关于消息组成部分的注意事项 我本来可以只更改绑定,就像我从 RPC/编码转到 RPC/所做的那样。它将是合法的 WSDL。然而,WS-I 基本概要(WS-I Basic Profile)(请参见 参考资料)规定文档/文字的消息的组成部分引用元素而不是类型,所以我遵循了 WS-I(并且此处使用元素部分可以很好地把我们带到关于文档/文字包装的样式的讨论)。 | | 下面是这种方法的优点和缺点: 优点
- 没有编码信息
- 您可以在最后用任何 XML 检验器检验此消息的有效性。
soap:body ( <xElement>5</xElement> )中每项内容都定义在 Schema 中。 缺点
- WSDL 变得有些复杂。不过,这是一个非常小的缺点,因为 WSDL 并没有打算由人来读取。
- SOAP 消息中缺少操作名。而如果没有操作名,发送就可能比较困难,并且有时变得不可能。
文档/文字的样式看起来似乎只是重新安排了 RPC/文字的模型的优点和缺点。您可以检验消息的有效性,但是您失去了操作名。有没有一种方法可以改进这一点呢?有的。它就是文档/文字包装的样式。
文档 在我说明文档/文字包装的样式的含义之前,让我给您展示 清单 8和 清单9中的 WSDL 和 SOAP 消息。 清单8. 用于 myMethod 的文档/文字包装的 WSDL <types> <schema> <element name="myMethod"/> <complexType> <sequence> <element name="x" type="xsd:int"/> </sequence> </complexType> </element> </schema> </types> <message name="myMethodRequest"> <part name=" parameters" element=" myMethod"/> </message> <message name="empty"/> <portType name="PT"> <operation name="myMethod"> <input message="myMethodRequest"/> <output message="empty"/> </operation> </portType> <binding .../> <!-- I won't bother with the details, just assume it's document/literal. --> | WSDL Schema 现在把参数放在包装中(请参见 清单9)。 清单:9. 用于 myMethod 的文档/文字包装的 SOAP 消息 <soap:envelope> <soap:body> <myMethod> <x>5</x> </myMethod> </soap:body> </soap:envelope> | 注意到此 SOAP 消息看起来非常类似于 RPC/文字的 SOAP 消息。您可能会说,它看起来与 RPC/文字的 SOAP 消息是完全一样的,不过,这两种消息之间存在着微妙的区别。在 RPC/文字的 SOAP 消息中, <soap:body> 的 <myMethod> 子句是操作的名称。在文档/文字包装的 SOAP 消息中, <myMethod> 子句是单个输入消息的组成部分引用的元素的名称。因此,包装的样式具有这样的一个特征,输入元素的名称与操作的名称是相同的。此样式是把操作名放入 SOAP 消息的一种巧妙方式。 文档/文字包装的样式的特征有: - 输入消息只有一个组成部分。
- 该部分就是一个元素。
- 该元素有与操作相同的名称。
- 该元素的复杂类型没有属性。
下面是这种方法的优点和缺点: 优点
- 没有编码信息。
- 出现在 soap:body 中的每项内容都是由 Schema 定义的,所以您现在可以很容易地检验此消息的有效性。
- 方法名又出现在 SOAP 消息中。
缺点
- WSDL 甚至更复杂,但是这仍然是一个非常小的缺点。
如您所见,文档/文字包装的样式还是有一些缺点,不过与优点比起来,它们都显得无足轻重。 | RPC/文字包装的样式? 从 WSDL 的角度来看,没有理由只是把把包装的样式和文档/文字绑定联系在一起。它可以很容易地应用于 RPC/文字绑定。但是这样做是相当不明智的。SOAP 将包含操作的一个 myMethod 元素和元素名称的子 myMethod 元素。另外,即使它是一个合法的 WSDL,RPC/文字元素部分也不遵循 WS-I。 | | 文档/文字的样式在哪里定义 这种包装的样式来源于 Microsoft。没有定义这种样式的规范;所以虽然这种样式是一个好的东西,但不幸的是,为了与 Microsoft 和其他公司的实现进行互操作,现在惟一的选择就是根据 Microsoft WSDL 的输出来猜测它是如何工作的。文档/文字包装的样式也实现在 IBM WebSphere SDK for Web Services 中(请参见 参考资料)。在这个示例中,样式是相当明显的;但是也存在个别情况,在这些情况中,由于缺少定义而导致需要操作的适当事项不够特别清晰。我们希望看到的最理想的情况就是将来能有像 Web 服务互操作组织(Web Services Interoperability Organization)这样的独立团体来帮助对此进行稳定化和标准化。
为什么不始终采用文档/文字包装的样式 至此,本文已经给了您这样的一个印象,文档/文字包装的样式是最好的方法。而实际的情况往往确实如此。不过,仍然存在着一些情况,在这些情况下,您最好是换一种别的样式。 采用文档/文字非包装的样式的理由 如果您已经重载了操作,就不能采用文档/文字包装的样式。 想象一下,除了我们一直在使用的方法之外,还有另一种方法,请参见 清单10。 清单10. 用于文档/文字包装的问题方法 public void myMethod(int x); public void myMethod(int x, String y); | WSDL 允许重载的操作。但是当您添加包装的样式到 WSDL 时,需要元素有与操作相同的名称,并且在 XML 中不能有两个名称相同的元素。所以您必须采用文档/文字非包装的样式或某种 RPC 样式。 采用 RPC/文字的样式的理由 由于文档/文字非包装的样式没有提供操作名,所以在有些情况下,您将需要采用某种 RPC 样式。比如说 清单11中的一组方法。 清单11. 用于文档/文字非包装的样式的问题方法 public void myMethod(int x); public void myMethod(int x, String y); public void someOtherMethod(int x); | 现在假定您的服务器接收到文档/文字的 SOAP 消息(您可以回过头在 清单 7中看一看它)。服务器应该发送哪一种方法呢?所有您能确切知道的就是,它一定不是 myMethod(int x, String x) ,因为消息只有一个参数,而这种方法需要两个参数。它可能是其他两种方法中的一种。采用文档/文字的样式,您没有办法知道是哪一种方法。 假定服务器接收到一个 RPC/文字的消息(比如 清单5中的),而不是文档/文字的消息。对于这种消息,服务器很容易决定把它发送到哪一种方法。您知道操作名是 myMethod,并且也知道只有一个参数,所以它必定是 myMethod(int x) 。 采用 采用 RPC/编码的理由有很多。其中两个主要的原因是: 数据图形 设想您有一个二进制树,其中的节点定义在 清单12中。 清单12. 二进制树节点 Schema <complexType name="Node"> <sequence> <element name="name" type="xsd:string"/> <element name="left" type="Node" xsd:nillable="true"/> <element name="right" type="Node" xsd:nillable="true"/> </sequence> </complexType> | 根据这种节点定义,我们可以构造一个树形结构,它的根节点 A 通过它左边和右边的的链接可以指向节点 B(请参见 图1)。 发送数据图形的标准方式是使用 href 标记,它是 RPC/编码的样式( 清单13)的一部分。 清单:13. RPC/编码的二进制树 <A> <name>A</name> <left href="12345"/> <right href="12345"/> </A> <B id="12345"> <name>B</name> <left xsi:nil="true"/> <right xsi:nil="true"/> </B> | 在任何文字的样式中,href 属性都是不可用的,这样图形链接就不再起作用了( 清单14和 图2)。您仍然有一个根节点 A,它从左边指向一个节点 B,从右边指向另一个节点 B。这两个 B 节点是等同的,但它们不是相同的节点。是复制了数据而不是引用了两次数据。 14. 文字二进制树 <A> <name>A</name> <left> <name>B</name> <left xsi:nil="true"/> <right xsi:nil="true"/> </left> <right> <name>B</name> <left xsi:nil="true"/> <right xsi:nil="true"/> </right> </A> |
在文字样式中,您可以通过各种方法构造图形,但是却没有标准的方法;所以您做的任何事情很可能不能与网络中其他端点上的服务进行互操作。 多态性 看一看 清单15中使用多态性 Schema 的 WSDL。 清单 15. 一个多态性 WSDL 的示例 <types> <schema> <complexType name="animal"> <sequence> <element name="name" type="xsd:string"/> </sequence> </complexType> <complexType name="dog"> <complexContent mixed="false"> <extension base="animal"> <sequence> <element name="breed" type="xsd:string"/> </sequence> </extension> </complexContent> </complexType> </schema> </types> <message name="in"> <part name="trainee" type="animal"/> </message> <message name="empty"/> <portType name="AnimalTrainer"> <operation name="train"> <input message="in"/> <output message="empty"/> </operation> </portType> | 当您把一个 dog 的实例传送给 train 操作时,所生成的 SOAP 消息必须包含类型编码信息,这样接收终端才能知道它所接收的是 animal 的哪一个扩展(请参见 清单16)。这种类型编码信息可用在 RPC/编码的样式中。 清单 16. 一个多态性 SOAP 消息 <soap:envelope> <soap:body> <train> <trainee xsi:type="Dog"> <name>Bob</name> <breed>Bloodhound</breed> </trainee> </train> </soap:body> </soap:envelope> |
总结 有四种绑定样式(其实真正有五种,不过文档/编码的样式没有什么意义)。虽然每种样式都有自己的用武之地,但是在大多数情况下,最好的样式是文档/文字包装的样式。
参考资料
关于作者 | | | Russell Butek 是 IBM WebSphere Web 服务引擎的一名开发人员。他同时也是 JAX-RPC Java Specification Request(JSR)专家组中 IBM 的代表。他参与实现了 Apache 的 AXIS SOAP 引擎,并且推动 AXIS 1.0 遵循 JAX-RPC 1.0。此前,他是 IBM CORBA ORB 的一名开发人员和许多 OMG 特别工作组(他担任主席职务)、核心特别工作组和互操作性特别工作组中 IBM 的代表。您可以通过 butek at us.ibm.com 与 Russell 联系。 | |