将Web服务组织在一起来创建跨组织的商业过程,要求所有的参与者以同样的标准模型进行编程并且避免提供专有的实现。经过多年来的众多厂家之间在标准协议上来提高互操作性的一致努力,在这方面已经取得了明显的进步。然而,实现Web服务间的无缝交互这一终极目标仍然是倍受关注的热门话题。研究了跨平台Web服务集成所面临的几个常见的互操作问题的根源,作者分析认为多个互操作问题都是源于J2EE技术和.NET之间的交互类型、基本数据类型和结构以及命名空间问题。Wangming Ye提供了最佳实践使得您可以避免这些问题,并提高成功集成的几率。本系列文章的第1部分着重强调了WSDL设计的重要性,并分析了Web服务集成中传统的RPC/encoded方式的优缺点。
引言
Web服务希望并且承诺将分散的应用程序以一种无缝的方式进行集成。但企业应用程序是在不同的平台上采用不同的技术构建的,因此,跨业务的集成并不是一件轻而易举的事。最近出现的基于Web服务的业务流程执行语言(BPEL)为定义Web服务的行为提供了一个高层描述语言。它提供一个标准和可移植的语言来将多个Web服务融合到一个商业流程中。 由于BPEL受到一些主要厂商的欢迎,一些用来自动设计商业流程的集成开发工具,如IBM WebSphere Studio Application Developer Integration Edition (Application Developer) 已经进入市场。这些工具减少了Web服务集成时需要的大量编码工作,并允许您通过拖拽WSDL文件到工具中的方式构建商业流程。我们期望这些工具能够自动产生调用Web服务的客户端的存根。 因此,集成的成功目前很大程度上一来于底层复杂的工具支撑。这就更加要求开发成员采用最佳实践来确保共享Web服务的内在互操作性。
正如我在本文以及随后的文章中所讲的,以下几个问题需要特别关注:
·利用厂商工具根据实现代码得到WSDL中的Web服务语言非常方便。但是这种方式忽略了消息脚本的设计,但这一点正是异构的环境(如J2EE技术对.NET)中Web服务互操作的关键。
·流行的RPC/encoded方式以其简单、灵活且熟悉的特点对开发人员是个有吸引力地选择。然而,在厂商之间对抽象SOAP编码数据模型的同步实现带来的困难成为Web服务互操作的一个困难性的挑战。
·弱类型的集合对象、包含空元素的数组和特定的本地数据类型都给互操作性带来一定的问题,特别是:
对于厂商提供的工具来讲,准确地解释代表弱类型集合对象的XML脚本并将它们映射到本地数据类型是不可能的。
带有空元素的数组的XML表示在.NET和Websphere之间是不同的。
由于本地数据类型和XSD数据类型并不具有一一对应关系,在转换过程中,信息或精度将会丢失。
·由于使用相对URI引用,Java技术和.NET之间不同命名转换将会导致命名空间冲突。
从WSDL开始
所有Web服务的互操作问题都可以归结到WSDL文件上。WSDL是Web服务的接口定义语言(IDL)和服务器与客户间的协议。服务语法,即WSDL文件中的消息类型、数据类型和交互方式,是构建松耦合的系统的关键。尽管WSDL并不要求使用特定的类型系统,XML脚本数据类型(XSD)在Web服务领域内广受欢迎。
XSD提供了大量的内置的基本类型并允许服务提供者进一步定义定制的复杂类型。XSD的类型系统比任何编程语言的类型系统都更加复杂和强大,并且更重要的是,它是独立于编程语言的,因此,它成为WSDL定义Web服务语法的逻辑起点。
理论上讲,就像COM和CORBA的IDL,您应该首先创建并编辑WSDL–在您基于WSDL中的服务语法来构建Web服务和特定实现语言的客户程序之前,定义接口、消息和数据类型。
然而,事实上,即使经验丰富的都不会那么做。他们最初开始实现特定于语言的接口,如Java接口,然后依赖厂商的工具从它们的实现代码中获取WSDL服务语法;再把这些WSDL服务语法交给Web服务客户程序以便根据WSDL找出如何来调用Web服务。最后,在他们经常不需要知道WSDL之前, 他们就能够创建并运行复杂的Web服务。他们可能在异构的环境中运行他们的客户和服务器程序,即或者J2EE平台,或者.NET平台,或者二者都有。
目前厂商的集成开发工具下面的方向发展,即所有的东西,包括WSDL都可以自动生成,不需要写任何代码(或者,至少许多厂商都声称并希望如此)。这一趋势很有吸引力,因为它开发效率很高,且相对于人工编码它很少出错(当然在工具功能合适且可靠的条件下)。在单一的平台上,互操作性一般不成问题,只要Web服务和客户都使用同样的开发工具。
当对跨越异构的环境产生互操作性要求时,问题变得比较明显。现在,您从实现语言来创建Web服务交互的关键构件,然后利用平台独立的工具将它们映射到WSDL中语言独立的元素。当客户平台上的工具根据 工具生成的WSDL来产生服务代理时,将进行另外一个映射。这一流程基于下面的事实,WSDL是Web服务的接口定义语言。语言独立的WSDL应该成为客户和服务器的共同基础。以这种方式使用开发工具将双倍增加映射过程中信息丢失的可能。
这并不意味着您应该放弃这些工具,所有的厂商应该停止生产并在市场上销售这些工具。在当前的企业应用程序开发领域自动化已经成为一个重要的因素。工具本身很强大,关键在您怎么利用它们的力量?您可以利用这些工具产生一个框架WSDL来作为起点或者模板。脚本、消息部件和数据绑定必须仔细设计来遵循WS-I一致性要求。就像您出于数据库效率的考虑而不希望由工具产生数据库脚本一样,您不应该忽视手工设计高效Web服务消息和数据类型。有时,即使元素的名字也要仔细设计来遵循潜在的异构平台间的命名转换。对于Web服务互操作性来讲,WSDL是唯一比较重要的构件。程序员必须学会基于原始XML消息进行编程,至少学会读取XSD、WSDL和SOAP消息。
仍旧采用RPC/encoded?
WS-I基本规范提出采用文本XML实现互操作。它禁止对soap:Envelope或派生的soap:Body元素使用soap:encodingStyle属性。因此,RPC/literal和Document/literal是WS-I标准唯一支持的2种格式。但是有许多Web服务工具不支持RPC/literal,因此,它将Document/literal作为唯一的实际互操作性标准。
然而,采用RPC/encoded方式来处理在XML脚本或WSDL出现之前就已经存在很久;另外,一些编码规则仍旧不能够采用XSD来表述。尽管SOAP编码被认为Web服务互操作性问题的主要根源,以及ASP.NET WebMethod基础设施缺省采用Document/literal,直到现在,大部分J2EE Web服务缺省采用RPC/encoded方式。
RPC/encoded方式受欢迎主要因为对于习惯了远程过程调用惯例的程序员来说是较为简单的编程模型。RPC绑定方式采用方法名称和参数来产生代表方法调用堆栈的结构,因此,它使Web服务看起来像一个带有封装对象的单一的逻辑组件,所有操作都在SOAP RPC堆栈中处理。这一点与Document/literal方式相反,在那种方式下,程序员不得不处理所有的事务,包括基于XML的SOAP消息的序列化和逆序列化。
另外,SOAP编码规则定义了标准,方便了从编程类型到XML的映射。编码规则非常灵活并支持图形数据和多态的表示。因此,它毫无疑问比Document/literal更适合,后者依赖于自然树结构来表示数据对象。
为了对RPC/encoded能做什么、不能做什么有个清晰的概念,让我们看一个示例。 Serializable Person类定义了一个它自己的友元,如清单1所示。
清单1. 自引用的Person类
public class Person implements java.io.Serializable {
private Person friend;
private String name;
public Person getFriend() {
return this.friend;
}
public void setFriend(Person friend) {
this.friend = friend;
}
<!– Other setter and getter methods –>
}
如果您初始化2个Person:A和B,并使Person A成为Person B的友元,使Person B成为Person A的友元,那么您创建了一个循环的对象图:
清单2. makeFriend方法
public Person makeFriend(Person A, Person B) {
A.setFriend(B);
B.setFriend(A);
return A;
}
如果您以Document/literal Web服务提供清单2中的方法,那么在XML中怎样表示循环对象呢?
事实证明利用Document/literal方式没有方便的方法来实现这一点。原因在于: Document/literal方法基于XML脚本定义了消息类型作为固定类型。XML脚本利用XSD基本类型作为叶子节点来表示自然树结构。一个循环对象图不能够转换为树结构。在这种情况下,由于Person类的示例A和B之间的循环引用,对象A和B的序列化成为递归循环。利用WebSphere Studio,最终您将看到堆栈溢出意外。这并不意味着Document/literal方法没有办法来解决它,但是需要开发人员更加深入的工作。
如果您采用SOAP编码作为消息绑定,通过在循环图上应用SOAP编码规则的多引用,循环引用可以很简单地表示。通过在WebSphere平台上运行Web服务来响应将John和Jason互相成为友元的SOAP请求,可以看一下序列化XML的载荷。 清单3显示了循环图是多么容易表示。
清单3. WebSphere的SOAP编码响应消息
<soapenv:Body soapenv:encodingStyle=”http://schemas.xmlsoap.org/soap/encoding/”>
<makeFriendResponse >
<makeFriendReturn href=”#id0″ />
</makeFriendResponse>
<multiRef id=”id0″ soapenc_root=”0″
soapenv:encodingStyle=”http://schemas.xmlsoap.org/soap/encoding/” xsi_type=”ns-
520570027:Person” >
<name xsi_type=”xsd:string”>John</name>
<friend href=”#id1″/>
</multiRef>
<multiRef id=”id1″ soapenc_root=”0″
soapenv:encodingStyle=”http://schemas.xmlsoap.org/soap/encoding/” xsi_type=”ns-
520570027:Person” >
<name xsi_type=”xsd:string”>Jason</name>
<friend href=”#id0″/>
</multiRef>
</soapenv:Body >
注意SOAP怎样使用本地id0和id1的类型ID来对编码元素Person A(John)和Person B(Jason)定义唯一的标志符,另外一个本地unqualified属性href用来定义这两个ID值的引用。对象A和对象B之间的循环引用通过灵活的SOAP编码规则可以很方便地表示。
RPC/encoded模型的能力对于传统的面向对象的程序员具有巨大的吸引力,它们已经习惯了在单一的领域内编程,如J2EE技术或.NET。当范围被限定在一个单一的平台,SOAP消息的编写者和阅读者具有同步的存根来理解编码的SOAP消息,尽管RPC/encoded模型为Web服务的设计者和实现者提供了很大的方便。但当Web服务跨平台时,它将带来巨大的代价。面对跨平台的互操作的要求,它的强大的能力反而变成弱点。为了说明互操作问题,仔细看一下Application Developer从前面Web服务生成的WSDL文件(参阅清单4)。
在WSDL文档中,操作makeFriend的输出消息makeFriendResponse有一个Person类型的消息部件makeFriendReturn。根据脚本,复杂类型Person必须有一个xsd:string类型名字的直接子元素,和一个Person类型的友元,并且不能有其它类型和属性。然而,序列化的SOAP响应消息(清单3)当然不符合这个规则。响应消息中的href没有在定义中出现。对于这个问题,怎样告诉接收者元素A和B是Person类的实例呢?
事实上,如果您编写一个.NET客户来与这个Web服务交互,.NET客户将不能够正确地逆序列化 清单3所示的WebSphere响应。.NET客户期望的描述John和Jason是友元的响应消息如下所示:
清单5:来自.NET的SOAP编码响应消息
<soap:Body
soap:encodingStyle=
“http://schemas.xmlsoap.org/soap/encoding/”><tns:makeFriendsResponse>
<makeFriendsResult href=”#id1″ />
</tns:makeFriendsResponse>
<types:Person id=”id1″ xsi_type=”types:Person”>
<name xsi_type=”xsd:string”>John</name>
<friend href=”#id2″ />
</types:Person>
<types:Person id=”id2″ xsi_type=”types:Person”>
<name xsi_type=”xsd:string”>Jason</name>
<friend href=”#id1″ />
</types:Person></soap:Body>
正如您看到的,多引用访问器编码是很强大的,但是它很难在XSD中表达。因此在不同的平台之间的实现有些细微的不同,如清单3和清单5所示。示例说明了SOAP编码和XML脚本校验之间的不同。Document/literal通过文本文档来传递信息并基于XML脚本来校验和逆序列化对象的XML表示。不同于Document/literal,RPC/encoded模型使用SOAP编码规则来表述抽象SOAP数据模型,并依赖厂商的SOAP库来提供抽象数据模型的具体实现。它是高度平台独立的。它不需要中间的规范来填补空白,因此,这种方式对于不同厂商的实现是开放的。尽管SOAP是标准,由于缺少XML脚本支持,SOAP实现(如Application Developer V4和V5的基于DOM的Apache SOAP库和基于SAX的Apache AXIS库、MS .NET 1.1框架的MS SOAP工具包 )还是有细微不同。
利用WS-I一致性测试工具检测一致性
WS-I基本规范为遵守Web服务规范来构建互操作的Web服务提供了指导。WS-I工作组开发了WS-I一致性测试工具来帮助开发人员判断它们的Web服务是否遵循规范指南。WS-I 测试工具分析器可以产生一个规范一致性报告来记录您的WSDL文档的一致性声明。目前,测试工具的C#和Java版本都可以下载。通过运行测试工具,WS-I基本规范1.0的大部分明显的冲突都会被报告。清单4中的WSDL文档的一致性检查测试的冲突如清单6中所示。
清单6. 从WS-I测试工具获取的失败报告
Result: failed
Failure Message: The use attribute of a soapbind:body, soapbind:fault,
soapbind:header and soapbind:headerfault does not have value
of “literal”.
Failure Detail Message:
SOAPBody ({http://schemas.xmlsoap.org/wsdl/soap/}body):
required=null
use=encoded
encodingStyles=[http://schemas.xmlsoap.org/soap/encoding/]
namespaceURI=http://cyclic.test
Element Location:
lineNumber=87
失败消息是自我解释的:soapbind:body、soapbind:fault、soapbind:header或soapbind:headerfault中的”use=encoded”被认为是不遵守WS-I规范的。您应该使用”use=literal”来代替。由于毫无疑问要使用Document/encoded,也就是说,您不应该使用RPC/encoded方式。
尽管WS-I一致性测试工具并不能够捕捉到Web服务互操作相关的所有问题,但它仍旧是个很强大的工具。与开发Web服务一起,不断开发复杂的测试工具包来捕捉潜在的互操作问题是一个很好的实践。
结束语
作者强调了在真正实现WSDL中的Web服务语法之前必须仔细设计,解释了为什么SOAP编码是Web服务互操作的主要障碍,并演示了构建复杂测试包的重要性来测试WS-I规范的一致性。在随后的系列文章中,作者将讨论由inbound和outbound数据类型和命名空间转换而导致的互操作性问题。
关于作者
Wangming Ye是IBM认证企业的开发人员和SUN认证的J2EE技术企业架构师。他最初在Transarc公司(后并入IBM)的DCE/DFS部门做开发人员,后来成为WebSphere Edge Server开发小组的WebSphere Content Distribution Framework的主要开发人员。目前他在IBM商业伙伴技术支持组织的Websphere Competency Center为Websphere商业伙伴提供技术支持。您可以通过yme@us.ibm.com与他联系。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
作者
相关推荐
-
SAP收购CallidusCloud 与Salesforce竞争
一直被称为后台办公巨头的SAP现在似乎也想在前台办公大展拳脚。 最新的迹象是SAP收购CallidusClou […]
-
API设计如龙生九子 各不相同
IT咨询管理公司CA Technologies对API产业做了个问卷调查,问卷内容涉及API设计风格以及管理部署的新动向。调查结果表明,JSON与XML可谓两分天下。
-
API设计:如何正确开发应用程序接口
在交互组件化软件的世界里,没有比让组件之间以及组件与移动设备和浏览器之间进行连接的应用程序接口(API)更重要的东西了。
-
从头开始实现领域驱动设计
领域描述业务;它是驱动企业的概念和逻辑的集合。如果遵循领域驱动设计(DDD)这一本质,那么领域就是应用程序中最重要的组成部分。