UML软件工程组织

 

 

与 WS-I Basic Profile 一致的 WSDL 设计 XML 模式注意事项
 
Shawn X. K. Hu (shu@xtensible.net), 解决方案架构师, Xtensible Solutions
 

本文内容包括:

有效的 XML 模式不一定在 WSDL 定义中有效。了解 XML 模式定义(XML Schema Definition,XSD)对 Web 服务设计有何重大影响。我们还提供了一些示例 XSD 和 WSDL,并将对其进行简单的讨论。

 引言

可以采用很多种方式来创建 XML 模式,以使其与 W3C XML 模式规范相符。不过,有效的模式并不一定意味着其对特定的 Web 服务描述语言(Web Service Description Language,WSDL)定义也有效。本文说明了如何为 Web 服务设计恰当的构造模式,以使其同时符合 W3C WSDL 规范 (WSDL v1.1) 和 Web 服务基本概要(WS-I Basic Profile,BP v1.1)。

在 WSDL 定义中,存在两种消息绑定样式:Document 和远程过程调用(Remote Procedure Call,RPC)。在 WS-I Basic Profile (BP) 中,对消息绑定样式进行了进一步限制。例如,SOAP 主体中的 RPC-literal 绑定必须引用 wsdl:part 中的“type”属性 (BP R2203),而 Document 绑定必须同“element”属性一起使用 (BP R2204)。属性“element”引用 XML 模式元素,而属性“type”则指示 XSD 中的 simpleType 和 complexType。简而言之,Document 样式的消息基于 XML 模式元素定义,而 RPC 消息则使用 XML 模式类型定义。

而且,只有全局级别的元素 (BP R2206) 和类型能够在 WSDL 定义中定义。这些元素和类型是 XSD 中的 <schema> 的直接子项。所有非直接子项组件都是本地的,通常嵌套在另一个模式组件中。此处的组件将引用模式元素、complexType 或 simpleType。

将在以下各个部分中讨论三个主要 XML 模式设计模式以及其他样式:

  • Russian Doll(意为“俄罗斯娃娃”)
  • Salami Slice(意为“意大利香肠片”)
  • Venetian Blind(意为“软百叶窗”)

Russian Doll

Russian Doll 模式采用的是仅具有一个全局元素的嵌套结构,如以下示例中的元素“Employee”。所有其他组件都封装在根元素中,均已本地化。

清单 1. 采用 Russian Doll 模式的示例 XSD

<xs:element name="Employee">
<xs:complexType>
<xs:sequence>
<xs:element name="ErpPerson">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="mName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ErpAddress">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element name="streetNumber" type="xs:string"/>
<xs:element name="streetName" type="xs:string"/>
<xs:element name="suiteNumber" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="stateOrProvince" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="postalCode" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>

在这种情况下,能在 WSDL 定义中引用的元素只有根元素“Employee”。因此,此模式仅能用于在 WSDL 中定义 Document 样式的 Employee 消息。下面列出了示例 WSDL <message> 和 <binding>,且具有名为 publishEmployeeService 的操作,此操作的输入消息基于 Employee 模式。

清单 2. Document 样式的 Employee 消息的 WSDL 定义


<wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" element="typeIn:Employee">
<wsdl:documentation>publish employee data</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/>
<wsdl:input name="employee">
<soap:body use="literal"/>
</wsdl:input>

</wsdl:operation>
</wsdl:binding>

请注意,wsdl:binding 部分中的 wsdl:input "name" 属性的值与 wsdl:message 中的“name”属性的值相同,而后者引用了 Employee 元素。typeIn 是输入模式 targetNamespace 的前缀,而 Employee 为模式的根元素。

Russian Doll 模式提供了具有最低扩展性的模式结构。在上面列出的示例中,除了根元素之外的所有组件均已本地化,不能进行重用。此模式适合在 Document 样式的 WSDL 中使用,但并不适合 RPC 样式,因为未定义全局级别的类型。同样,“类型”也是 WS-I BP 对 RPC 绑定样式的一个限制。

此外,此模式还不用于 WSDL 中可能需要扩展元素的情况,如为消息定义使用模式元素 ErpAddress 时。ErpAddress 是 XML 模式中的一个元素,但是并不位于全局级别。

Salami Slice

顾名思义,Salami Slice 模式将 XML 模式划分为具有更多全局级别元素的“片”。此模式非常适合 Document 样式的 WSDL,其灵活性高于 Russian Doll 样式。以下模式包含与 Russian Doll 模式相同的 Employee 信息,但采用不同的方式对其进行表示。

清单 3. 采用 Slice pattern 模式的示例 XSD

<xs:element name="Employee">
<xs:complexType>
<xs:sequence>
<xs:element ref="ErpPerson"/>
<xs:element ref="ErpAddress"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ErpPerson">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="mName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ErpAddress">
<xs:complexType mixed="false">
<xs:sequence>
<xs:element name="streetNumber" type="xs:string"/>
<xs:element name="streetName" type="xs:string"/>
<xs:element name="suiteNumber" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="stateOrProvince" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="postalCode" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>

ErpPerson 和 ErpAddress 元素实际上可以进一步进行“切分”,以获得更多的元素粒度级别,但为了简单起见,我们仅到此为止。

现在 ErpPerson 和 ErpAddress 成为了模式中的全局级别元素,是能够在其他组件(如元素“Employee”)中引用的可重用组件。而且,使用 Salami Slice 模式的一个好处就是可以在 WSDL 定义中引用更多的元素。例如,现在可以在 WSDL 中将 ErpPerson 和 ErpAddress 分别用于为 ErpPerson 和 ErpAddress 有效负载定义 Document 样式的消息。

简要总结一下,Salami Slice 模式中的 Employee 模式可用于使用以下消息定义 WSDL:

  • Document 绑定样式的 Employee 消息
  • Document 绑定样式的 ErpPerson 消息
  • Document 绑定样式的 ErpAddress 消息

以下是上面所列出的内容对应的 WSDL 消息和绑定定义:

清单 4. Document 样式的 Employee 消息的 WSDL 定义


<wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" element="typeIn:Employee">
<wsdl:documentation>publish employee information</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/>
<wsdl:input name="employee">
<soap:body use="literal"/>
</wsdl:input>
….
</wsdl:operation>
</wsdl:binding>

清单 5. Document 样式的 ErpPerson 消息的 WSDL 定义


<wsdl:message name="publishErpPersonServiceRequest">
<wsdl:part name="erpPerson" element="typeIn:ErpPerson">
<wsdl:documentation>publish employee data</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/>
<wsdl:input name="erpPerson">
<soap:body use="literal"/>
</wsdl:input>
….
</wsdl:operation>
</wsdl:binding>

清单 6. Document 样式的 ErpAddress 消息的 WSDL 定义

… <wsdl:message name="publishErpAddressServiceRequest">
<wsdl:part name="erpAddress" element="typeIn:ErpAddress">
<wsdl:documentation>publish employee address data</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishErpAddressService">
<soap:operation style="document"/>
<wsdl:input name="erpAddress">
<soap:body use="literal"/>
</wsdl:input>

</wsdl:operation>
</wsdl:binding>

不过,Salami Slice 模式并不适用于 RPC 样式的 WSDL,因为其中未定义全局级别的类型(简单类型或复杂类型)。

Venetian Blind

在此模式中,模式中除根元素外的所有全局级别的组件都定义为类型。Venetian Blind 模式与 Russian Doll 类似,因为都使用的是单个全局元素。Venetian Blind 和 Salami Slice 模式的区别在于,所有全局组件(除根元素外)现在都定义为类型,而不是像 Salami Slice 模式中一样定义为模式元素。

Venetian Blind 通常可以用于 Document 和 RPC 绑定类型。以下模式表示了与之前的示例相同的 Employee 信息,但采用的是 Venetian Blind 模式。除了根元素“Employee”外,全局级别的复杂类型定义如下:

清单 7. 采用 Venetian Blind 模式的示例 XSD

<xs:element name="Employee" type="EmployeeType"/>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="ErpPerson" type="ErpPersonType"/>
<xs:element name="ErpAddress" type="ErpAddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ErpPersonType">
<xs:sequence>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="mName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ErpAddressType">
<xs:sequence>
<xs:element name="streetNumber" type="xs:string"/>
<xs:element name="streetName" type="xs:string"/>
<xs:element name="suiteNumber" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="stateOrProvince" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="postalCode" type="xs:string"/>
</xs:sequence>
</xs:complexType>

此模式可以用于在 WSDL 中定义 Document 和 RPC 样式的 Employee 消息,因为其中既包括全局级别的元素,也包括类型。不过,它并不支持 ErpPerson 和 ErpAddress 的 Document 样式的消息,因为二者都仅定义为了“complexType”。采用 Venetian Blind 模式的 Employee 模式可用于使用以下消息定义 WSDL:

  • Document 绑定样式的 Employee 消息
  • RPC 绑定样式的 Employee 消息
  • RPC 绑定样式的 ErpPerson 消息
  • RPC 绑定样式的 ErpAddress 消息

对应的 WSDL <message> 和 <binding> 定义如下:

清单 8. Document 样式的 Employee 消息的 WSDL 定义


<wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" element="typeIn:Employee">
<wsdl:documentation>publish employee information</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/>
<wsdl:input name="employee">
<soap:body use="literal"/>
</wsdl:input>
….
</wsdl:operation>
</wsdl:binding>

清单 9. RPC 样式的 Employee 消息的 WSDL 定义


<wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" type="typeIn:EmployeeType">
<wsdl:documentation>publish employee information</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="rpc"/>
<wsdl:input name="employee">
<soap:body use="literal"
namespace="http://someCompany.com/soa/2006-07-01/publishEmployeeService"/>
</wsdl:input>
….
</wsdl:operation>
</wsdl:binding>

清单 10. RPC 样式的 ErpPerson 消息的 WSDL 定义


<wsdl:message name="publishErpPersonServiceRequest">
<wsdl:part name="erpPerson" type="typeIn:ErpPersonType">
<wsdl:documentation>publish employee data</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishErpPersonService">
<soap:operation style="rpc"/>
<wsdl:input name="erpPerson">
<soap:body use="literal"
namespace="http://someCompany.com/soa/2006-07-01/publishErpPersonService"/>
</wsdl:input>
….
</wsdl:operation>
</wsdl:binding>

清单 11. RPC 样式的 ErpAddress 消息的 WSDL 定义


<wsdl:message name="publishErpAddressServiceRequest">
<wsdl:part name="erpAddress" type="typeIn:ErpAddressType">
<wsdl:documentation>publish employee address data</wsdl:documentation>
</wsdl:part>
</wsdl:message>

<wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishErpAddressService">
<soap:operation style="rpc"/>
<wsdl:input name="erpAddress">
<soap:body use="literal"
namespace="http://someCompany.com/soa/2006-07-01/publishErpAddressService"/>
</wsdl:input>
....
</wsdl:operation>
</wsdl:binding>

尽管 ErpPerson 和 ErpAddress complexType 都可以在 RPC 样式的 WSDL 中引用,但将其定义为没有对应模式元素的 complexType 的做法并不太好,因为 complexType 无法由有效负载进行实例化。

例如,模式中的 ErpPersonType complexType 为 ErpPerson 提供了一个数据类型,包括 lastName, firstName 和 mName。它旨在用于创建以下所示的 XML 有效负载:

清单 12. 示例 ErpPerson 消息有效负载

<ErpPerson>
<lastName>Smith</lastName>
<firstName>John</firstName>
<mName>K</mName>
</ErpPerson>

不过,Venetian Blind 样式的 XML 模式无法用于生成或验证此类有效负载,因为在模式中缺乏匹配的全局元素。为了在这种情况下解决此问题,请同时为 ErpPerson 定义元素和 complexType。

混合模式

您并不必完整地定义以上列出的所有三种模式。可以在需要的情况下混合使用这些模式。例如,某些组件可以定义为元素,而其他的仅定义为 complexType。从 XML 有效负载的角度而言,一个原则是,如果组件需要在 XML 有效负载中进行实例化,则应该将其定义为元素。否则,就可以将其定义为类型。

从 WSDL 的角度而言,如果组件在 Document 绑定样式中使用,则应将其定义为元素。否则就可以将模式组件定义为用于 RPC 绑定的类型。下面列出的模式满足了 WSDL 定义的以下要求:

  • Document 绑定样式的 Employee 消息
  • RPC 绑定样式的 Employee 消息
  • Document 绑定样式的 ErpPerson 消息
  • RPC 绑定样式的 ErpPerson 消息
  • Document 绑定样式 ErpAddress 消息

清单 13. 采用混合模式的示例 XSD

<xs:element name="Employee" type="EmployeeType"/>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element ref="ErpAddress"/>
<xs:element ref="ErpAddress"/>
</xs:sequence>
</xs:complexType>

<xs:element name="ErpPerson" type="ErpPersonType"/>
<xs:complexType name="ErpPersonType">
<xs:sequence>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="mName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="ErpAddress">
<xs:complexType>
<xs:sequence>
<xs:element name="streetNumber" type="xs:string"/>
<xs:element name="streetName" type="xs:string"/>
<xs:element name="suiteNumber" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="stateOrProvince" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="postalCode" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>

就 WSDL 中使用的 XML 模式元素和类型的可访问性而言,混合模式更为灵活。它允许您通过准确遵循在 Web 服务中选择的绑定样式构建 XML 模式。

仅类型

有时候,会仅在 XML 模式中定义全局级别的类型,而不定义任何全局元素。这种类型的模式通常作为基础模式使用,用以封装要在其他模式中引用的数据类型。以下是仅在全局级别包含了 complexType 的 Employee 模式:

清单 14. 仅具有全局级别 complexType 的示例 XSD

<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="ErpPerson" type="ErpPersonType"/>
<xs:element name="ErpAddress" type="ErpAddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ErpPersonType">
<xs:sequence>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="mName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ErpAddressType">
<xs:sequence>
<xs:element name="streetNumber" type="xs:string"/>
<xs:element name="streetName" type="xs:string"/>
<xs:element name="suiteNumber" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="stateOrProvince" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="postalCode" type="xs:string"/>
The corresponding WSDL <message> and <binding> definitions are listed below:on
</xs:sequence>
</xs:complexType>

根据 W3C 和 WS-I BP,此模式对 RPC 样式的 WSDL 定义有效。不过,没有全局元素的模式对 XML 有效负载生成和验证用处不大。

结束语

总而言之,在考虑 Web 服务设计时,需要恰当考虑模式的构造方式。如果 XML 模式要用于 Document 和 RPC 样式的 Web 服务,最好在模式的全局级别同时包含元素和类型。

参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
  • 了解有关 WS-I Basic Profile 的详细信息。
  • 阅读 Should it be an Element or a Type?
  • 了解何时应在全局或本地级别声明元素或类型
  • 阅读 developerWorks XML 专区的文章“创建灵活和可扩展的 XML 模式”。
  • 跟踪最新的 developerWorks 技术事件与网络广播。
  • 参阅演示文档“Using XML schemas effectively in WSDL design”。

获得产品和技术

  • 了解有关 W3C XML 模式规范的更多信息。
  • 阅读 W3C WSDL 规范。
  • 了解有关 WS-I 测试工具的信息。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号