Web服务是Java™技术在企业计算中的重要角色。 在本系列文章中,XML和Web服务顾问Dennis Sosnoski涵盖了对使用Web服务的Java开发人员很重要的主要框架和技术。 跟随该系列文章以了解该领域的最新进展,并了解如何使用它们来帮助您的编程项目。
企业应用程序的Web服务在很大程度上依赖于服务定义的使用。 服务定义指定了服务提供者与任何潜在使用者之间的基本契约,详细说明了服务提供的功能类型以及作为每个功能的一部分交换的消息。 服务提供者和使用者可以自由地以他们想要的任何方式实现交换的目的,只要他们发送的实际消息与服务定义相匹配即可。 使用指定XML消息交换的服务定义可以使Web服务与分布式编程的早期技术区分开来。
已经提出了用于定义Web服务的各种技术,但是使用最广泛的方法是WSDL 1.1。 WSDL 1.1有一些缺点,包括一个过于复杂的结构,使初学者无法理解它。 它还缺乏权威的正式定义,从而导致连续的“附加”澄清工作弥补了原始规范文档中的某些漏洞。 作为响应,Web服务堆栈倾向于尽可能灵活地处理WSDL 1.1文档。 这种灵活性可能反过来加深了对WSDL 1.1理解的困惑,因为开发人员可以看到各种各样的WSDL结构,而没有关于哪种方法最好的指导。
在本文中,您将学习理解WSDL 1.1文档,并且将看到Java模型的第一部分,该模型用于验证WSDL文档并将其转换为标准格式。
本文使用:
wsdl前缀代表WSDL 1.1 http://schemas.xmlsoap.org/wsdl/名称空间 WSDL 1.1的SOAP 1.1扩展所使用的http://schemas.xmlsoap.org/wsdl/soap/名称空间的soap前缀 用于XML架构定义的http://www.w3.org/2001/XMLSchema命名空间的xs前缀在2001年初发布的WSDL 1.1在技术上已被2007年发布的W3C WSDL 2.0建议所取代。WSDL2.0提供了比WSDL 1.1更干净的结构,并且具有更大的灵活性。 但是WSDL 2.0遇到了鸡与蛋的问题— WSDL 2.0并未得到广泛使用,因为它没有得到广泛支持,并且由于没有得到广泛使用,因此对Web服务堆栈的实现者施加支持的压力很小。 WSDL 1.1尽管有缺陷,但对于大多数目的来说已经足够了。
最初的WSDL 1.1规范对于要使用多少个功能并不准确。 因为WSDL的重点是使用SOAP服务定义,所以它还包括对某些SOAP功能(例如rpc编码)的支持,这些功能后来被发现不受欢迎。 Web服务互操作性组织(WS-I)在基本概要(BP)中解决了这些问题,该概要定义了使用SOAP和WSDL的Web服务的最佳实践。 BP 1.0于2004年获得批准,并于2006年更新为BP 1.1。在本文中,我将基于WS-I BP指南介绍WSDL 1.1,而忽略了有效弃用的功能,例如SOAP的rpc编码。
XML架构定义应该定义XML文档的结构。 WSDL 1.1在原始规范中包含了一个模式描述,但是该模式在多个方面与文本描述不匹配。 稍后在模式的修改版本中对此进行了更正,但是WSDL 1.1文档未进行更新以反映此更改。 然后,WS-I BP组决定对WSDL模式进行更多更改,因此它创建了似乎是这种湿滑模式的最佳实践版本的版本。 写入一个模式的版本的文档通常与其他版本不兼容(尽管使用相同的名称空间),但是幸运的是,大多数Web服务工具基本上会忽略该模式并接受任何看起来合理的东西。 (请参阅相关主题的链接WSDL的许多模式。)
甚至WSDL 1.1模式的WS-I BP版本也无法确保WSDL 1.1文档符合规范。 该模式并不反映WS-I BP中的所有约束,尤其是在组件顺序方面。 除此之外,XML Schema无法处理许多类型的对文档的易于声明的约束(例如,替代属性或来自不同模式的必需扩展元素)。 因此,检查WSDL 1.1文档是否符合WSDL 1.1规范(已由WS-I BP修订)不仅仅涉及XML模式验证。 我将在本文后面再回到该主题。 首先,我将回顾WSDL 1.1服务描述的结构。
WSDL 1.1文档使用固定的根元素,方便地命名为<wsdl:definitions> 。 在此根元素内,WSDL 1.1命名空间中定义了一个“被动”子元素(仅引用单独的WSDL 1.1文档)和五个“活动”子元素(这些元素实际上有助于服务描述):
<wsdl:import>引用了一个单独的WSDL 1.1文档,并将其描述合并到该文档中。 <wsdl:types>定义用于消息交换的XML类型或元素。 <wsdl:message>根据XML类型或元素定义实际的消息。 <wsdl:portType>定义由服务实现的一组抽象操作。 <wsdl:binding>使用特定的协议和格式定义<wsdl:portType>的实际实现。 <wsdl:service>定义了一个整体的服务,通常包括一个或多个带有<wsdl:binding>元素访问信息的<wsdl:port> <wsdl:binding>元素。还有一个<wsdl:document>元素,可以用作文档用途,作为<wsdl:definitions>元素的第一个子元素,也可以用作上述任何元素的第一个子元素。
完整的服务描述通常需要这些元素中的至少一个,除了<wsdl:import> ,但是它们不必全部都出现在同一文档中。 您可以使用<wsdl:import>从多个文档中组装完整的WSDL描述,从而使您可以灵活地拆分描述以适合您的组织。 例如,前三个描述元素( <wsdl:types> , <wsdl:message>和<wsdl:portType> )一起提供了完整的服务接口描述(可能是由体系结构团队定义的),因此对将它们与面向实现的<wsdl:binding>和<wsdl:service>元素分开。 所有主要的Web服务堆栈都支持将描述拆分为多个WSDL文档。
清单1和清单2展示了一个WSDL服务描述的示例,该服务描述分为两个WSDL文档,其中接口描述组件位于BookServerInterface.wsdl文件中,实现组件位于BookServerImpl.wsdl中。 清单1显示了BookServerInterface.wsdl:
清单2显示了BookServerImpl.wsdl。 <wsdl:import>元素在其开始位置附近会从BookServerInterface.wsdl导入接口描述。
除了WSDL 1.1名称空间中的元素(和属性)定义之外,WSDL 1.1还定义了扩展元素。 这些扩展元素旨在插入WSDL 1.1服务描述中的特定位置,以便提供特定类型的服务所需的其他信息。 仍然被广泛使用的唯一WSDL 1.1扩展元素是用于SOAP 1.1绑定的元素(如清单2所示 ,在<wsdl:binding>和<wsdl:service>元素内),它们由原始WSDL 1.1规范定义,并且对于SOAP 1.2绑定,在2006年由另一个规范定义。
<wsdl:types>元素以一个或多个<xs:schema>元素的形式包装用于消息的所有XML定义。 (WSDL允许这些定义替代XML Schema,但是大多数堆栈仅支持XML Schema。) <xs:schema>元素可以使用<xs:import>和/或<xs:include>来合并WSDL外部的其他模式。 ,如果需要的话(以及引用同一WSDL中包含的独立模式)。
因为单个<wsdl:types>元素可以包含任意数量的模式定义,所以从没有理由在WSDL文档中使用多个<wsdl:types>元素。 在清单1中 , <wsdl:types>元素在BookServerInterface.wsdl的顶部附近。
除了<wsdl:import>和<wsdl:types> ,WSDL文档的所有其他顶级组件都是通过使用必需的name属性来单独命名的。 如果在文档根<wsdl:definitions>元素上使用targetNamespace属性(作为最佳实践,通常应这样做),则这些组件的名称在该目标名称空间中定义。 这意味着在定义名称时,只给出名称的简单或“本地”部分,但是对该组件的引用必须使用名称空间前缀或默认名称空间来限定名称。 图1显示了WSDL组件之间最重要的链接,实线代表合格的名称引用,虚线代表用于无名称空间限定的标识的名称:
由<wsdl:message>元素表示的<wsdl:message>是WSDL服务描述的核心。 <wsdl:message>元素是客户机和服务提供者之间交换的XML数据的描述。 每个<wsdl:message>包含零个或多个(通常一个) <wsdl:part>子元素。 每个part元素都需要有自己的name属性(在<wsdl:message>是唯一的)和引用XML数据的架构定义的element或type属性。 清单1中显示了几个<wsdl:message>元素,紧随BookServerInterface.wsdl中的<wsdl:types>元素。
<wsdl:portType>元素根据发送到服务和从服务发送的消息定义服务的抽象接口。 <wsdl:portType>元素包含任意数量的<wsdl:operation>子元素。 每个<wsdl:operation>子元素都需要有自己的name属性(WS-I BP要求在<wsdl:portType>是唯一的),并且包含一个或多个描述该操作使用的消息的子元素。 子元素分为三种类型,代表不同的用法类型:
<wsdl:input> :从客户端发送到服务提供者的数据,作为操作的输入 <wsdl:output> :服务提供者作为操作结果返回给客户端的数据 <wsdl:fault> :处理过程中发生错误时,服务提供商将数据返回给客户端WSDL 1.1定义了客户端和服务提供者之间交互的几种模式,以<wsdl:input>和<wsdl:output>子元素的不同序列表示,但是并不是所有的模式都定义得足够好可以实现。 WS-I BP将模式限制为两种:请求-响应操作(后跟<wsdl:input>和<wsdl:output> ,以及单向操作,只有<wsdl:input> 。 对于请求-响应操作(到目前为止,是最常见的类型), <wsdl:input>和<wsdl:output>元素后面可以跟任意数量的<wsdl:fault>元素。
每个<wsdl:input> , <wsdl:output>或<wsdl:fault>元素都通过必需的message属性引用消息描述。 该引用是名称空间限定的,因此通常需要包含前缀。 您可以在清单1中看到这样的示例,例如,在getBook操作说明中使用了<wsdl:input message="tns:getBookMessage"/>元素。 ( tns前缀是在根<wsdl:definitions>元素上<wsdl:definitions> ,该元素具有与targetNamespace属性相同的名称空间URI。)
您可以将<wsdl:portType>在大多数方面视为Java接口的逻辑等效项,其中<wsdl:operation>元素等效于方法, <wsdl:input>元素作为方法参数, <wsdl:output>元素作为方法返回,而<wsdl:fault >元素作为检查的异常。 与大多数从现有Java代码生成WSDL的工具一样,从WSDL生成Java代码也使用这些对应关系。
自从该规范于2000年发布以来,SOAP 1.1便已广泛用于Web服务。SOAP1.2是通过W3C在更广泛的行业支持下开发的,并于2007年作为W3C的正式标准发布。与SOAP 1.1相比,SOAP 1.2的文档记录更清晰,更干净,移除了1.1中一些较丑陋的方面。 尽管结构更简洁,但对于大多数Web服务而言,两者之间几乎没有实际差异。 SOAP 1.2的最重要的功能可能是它是使用XML二进制优化打包(XOP)和SOAP消息传输优化机制(MTOM)提供的对SOAP附件的增强支持的唯一官方支持的方法。 到目前为止,我已经在Java Web服务系列中使用了SOAP 1.1,因为某些较早的堆栈不支持SOAP 1.2,但是1.2可能是新Web服务开发的更好选择。
<wsdl:binding>元素表示由<wsdl:portType>定义的抽象接口的实例,如清单2所示 ,位于BookServerImpl.wsdl的开头。 type属性给出绑定实现的端口类型的限定名称。
<wsdl:binding>的子元素提供有关如何实现端口类型的详细信息。 从WSDL命名空间对应于那些的子元素<wsdl:portType>和必须使用相同的name的值-而不是名称空间限定的引用,与<wsdl:portType>参考。 图1以虚线显示了<wsdl:operation>级别的此连接。 名称相同的连接适用于<wsdl:operation>元素的<wsdl:input> / <wsdl:output> / <wsdl:fault>子元素。 尽管重复使用了相同的元素名称,但当它们是<wsdl:binding> <wsdl:portType>元素而不是<wsdl:portType>元素的子元素时,它们的内容却大不相同。
<wsdl:binding>是WSDL定义的扩展起作用的地方。 <soap:binding>子元素用于定义SOAP服务(WS-I BP允许的唯一服务类型,尽管WSDL 1.1也允许HTTP绑定)。 <soap:binding>元素使用必需的transport属性来定义<soap:binding>使用的transport类型。 (HTTP是WS-I BP允许的唯一选择,如清单2中的值http://schemas.xmlsoap.org/soap/http 所示 。)可选的style属性允许您在rpc和文档样式之间进行选择。用于XML数据表示(默认document最常见,与使用模式元素定义而不是类型定义的消息相对应)。
在<wsdl:binding>每个<wsdl:operation>子元素内,可以使用<soap:operation>元素指定SOAPAction值,以标识调用该操作的请求(并且还可能覆盖rpc或文档样式的选择由<soap:binding>元素确定,尽管WS-I BP禁止这种用法)。 每个<wsdl:input> / <wsdl:output> / <wsdl:fault>子元素都包含另一个扩展元素,在清单2的情况下,该扩展元素始终为<soap:body> (指示消息数据在SOAP中发送)消息正文-对于<wsdl:input>或<wsdl:output> ,或与a一起使用的等效<soap:fault> ,也有可能在SOAP标头中发送数据,甚至发送错误,尽管我认为这是一个糟糕的做法。 <wsdl:fault> 。
WSDL服务描述的最后一个组成部分是<wsdl:service>元素,它由一组<wsdl:port>元素组成。 每个<wsdl:port>元素都将访问地址与<wsdl:binding>关联。 访问地址由嵌套的<soap:address>扩展元素提供。
有了WSDL 1.1文档的模式和规则的所有变体,许多文档与WS-I BP定义的最佳实践形式不匹配也就不足为奇了。 所有Web服务堆栈的支持都与最佳实践形式有很多差异,这有助于永久使用过时或不正确的构造,从而导致不良行为在整个行业中传播。 而且我绝对不能避免这种传染-在回顾我为该系列示例代码提供的WSDL文档时,我惊讶地发现没有一个是完全正确的。
因此,当我着手撰写本文时,我认为最好包括一个人们可以用来对照最佳实践规则来验证WSDL文档的工具。 只要原始WSDL没有错误,将WSDL文档转换为最佳实践形式似乎只是一步。 事实证明,这比我最初计划的工作要多得多,该模型的全部细节将在本系列的下两篇文章中介绍。
许多不同的模型已经构建了与Java语言WSDL文件的工作,包括Java工具包(WSDL4J),这是JSR 110的参考实现(见广泛使用的Web服务描述语言相关主题 )。 这些模型似乎都不符合我想要实现的目标,因为双重目标是:首先,以任何中途的合理形式阅读WSDL文档,并报告最佳实践的错误和变化,其次,编写无错误的WSDL文档。重新格式化为最佳实践形式。 例如,WSDL4J不会保持输入中元素的顺序,这样我就可以报告排序问题,并且也无法处理架构定义,因此不能直接用于检查<wsdl:part>元素的引用。 因此,我可以选择更现实地设定目标还是编写自己的模型。 我自然选择编写自己的模型。
我在本文中使用“ 验证 ”一词指的是检查WSDL文档的正确性,因为替代术语“ 验证”通常与XML文档一起使用,意味着根据架构定义检查文档。
作为JiBX / WS项目的一部分,我以前实现了与JiBX数据绑定一起使用的部分WSDL模型。 该模型仅设计用于输出,它涉及相对较少的类,在某些情况下,这些类将WSDL XML结构的嵌套元素中的数据组合在一起( <wsdl:message>与单个<wsdl:part>子元素结合在一起, <wsdl:input> <wsdl:binding> <wsdl:input> , <wsdl:output>和<wsdl:fault>与<soap:body>或<soap:fault>元素组合,依此类推)。 这种紧凑的类结构使构建该结构所支持的WSDL文档的子集变得容易,但是当我最初考虑基于该模型建立验证和重构工具时,我意识到支持可能结构不良的WSDL的输入将需要一个更接近于该模型的模型。 XML表示形式。
从WSDL 1.1的WS-I BP模式生成代码是另一种选择。 当我看到它时,我意识到仅直接使用生成的类将是一团糟,因为该架构包含冗余类型以及一些用于表示不同消息交换模式的笨拙构造(某些构造后来被禁止) WS-I BP文字)。
因此,我最终只是手动构建了类,尽管最终结果与从架构生成的代码开始并消除了不必要的重复和复杂性几乎一样。 JiBX数据绑定支持对相同类的多个绑定,因此我能够设置输入绑定以处理任何版本的WSDL 1.1所允许的全部选项,同时将输出绑定配置为仅以最佳实践形式输出WSDL。 。
清单3显示了Definitions类的一部分,它对应于根<wsdl:definitions>元素:
清单3中的子元素数据的组织显示了模型如何支持通用形式的输入和最佳实践的输出。 它没有使用所有类型的子元素的单个列表,而是为每种类型使用了单独的列表。 输入的JiBX绑定将子元素视为无序集合,每次取消编组子元素时都会调用特定于元素类型的set-method。 设置方法将实例添加到类型列表中,而不是替换任何先前的值,正如您从用于<wsdl:message>子元素的addMessage()设置方法中可以看到的那样。 每个设置方法还运行状态检查以捕获元素未按预期顺序排列的情况。
在任何WSDL元素中都允许使用扩展属性和元素(基本上是不使用WSDL 1.1名称空间的任何属性或元素)。 本系列先前文章中的WSDL文档中嵌入的WS-Policy配置是此类扩展元素的一个示例,实际的策略参考也是如此。 这些扩展元素的最佳实践是让它们位于WSDL 1.1名称空间中的所有子元素之前,这就是在输出绑定中对其进行处理的方式。 输入绑定使用来自WSDL元素类的基类的代码处理扩展元素和属性( 清单3中未显示),并允许这些元素以任何顺序排列(如果它们遵循WSDL 1.1名称空间中的元素,则生成警告)。 。
该模型通过为每个扩展名称空间使用单独的绑定来处理已知的扩展元素,每个绑定都有自己的一组类。 在下一个Java Web服务部分中,我将更详细地介绍这些扩展元素的处理,该部分还将提供源代码的更多细节。
WSDL数据的一些基本验证是通过将与元素相对应的未编组对象添加到WSDL文档的树结构中来完成的,如清单3末尾的addMessage()代码所示 。 此代码使用checkAdd()方法检查子元素的顺序,并使用addName()方法确保提供了有效的名称(文本与NCName模式类型匹配,并且值在元素类型内是唯一的),并且将名称映射到对象。 但这仅是隔离地检查元素的最基本信息。 需要使用更多的验证代码来检查每个元素的其他属性以及元素之间的相互关系。
JiBX允许您在解组和编组过程中调用用户扩展挂钩。 WSDL模型使用一种这样的扩展挂钩(一种后置方法)来运行验证逻辑。 关联对象的解组完成后,将调用后置方法,因此,这通常是运行对象验证检查的好方法。 在验证WSDL的情况下,最简单的方法是针对根<wsdl:definitions>元素,以单个后置方法运行所有对象验证。 当组件未按预期顺序使用WSDL文档的组件时,此方法可以避免任何问题。
在本文中,您已经了解了WSDL结构和用法的基础知识,以及WSDL Java数据模型的介绍,该模型旨在支持验证WSDL文档并将其转换为最佳实践形式。
本系列的下一篇文章将进一步探讨该主题,研究编写WS-Policy和WS-SecurityPolicy断言时经常遇到的问题。 它还将更深入地介绍WSDL模型和验证处理,包括扩展模型以包括WSDL中嵌入的WS-Policy / WS-SecurityPolicy断言。
翻译自: https://www.ibm.com/developerworks/java/library/j-jws20/index.html
相关资源:wsdl.exe 生成代理文件