早先的一些blog文章已经说了很多关于Web Service客户端在同一种语言实现或者不同语言实现中由于细节实现不同导致兼容性的问题。昨天解决的问题在一个边界问题上又一次说明了这种异构环境的互通标准在不同的实现当中,由于细节理解不同会造成一些兼容性问题。
问题:
当接口返回类型为一个对象,同时这个对象中有一个属性是数组类型,那么这个数组类型的对象在返回的SOAP消息中就无法正确解析。如下定义了一个对象:
publicclass DemoStruct implements Serializable
{
private RecordType[] recordArr;
private String name;
public RecordType[] getRecordArr(){returnrecordArr;}
publicvoid setRecordArr(RecordType[] recordArr){this.recordArr = recordArr;}
public String getName(){returnname;}
publicvoid setName(String name){this.name = name;}
}
接口定义如下:
publicinterface IDemoTest
{
DemoStruct queryAccountRecord(String account);
}
问题分析:
在早先返回数组对象的问题上已经解决了,但是当数组归属于对象,作为一个属性的时候,出现了问题。
首先在wsdl中定义一个类型是数组有两种方式:
1. 定义maxoccurs=”unbounded”。
<xsd:complexType name="ArrayOfRecordType">
<xsd:sequence>
<xsd:element maxOccurs="unbounded" minOccurs="0" name="RecordType" nillable="true" type="ns1:RecordType"/>
</xsd:sequence>
</xsd:complexType>
2. 定义soap encoding中的arrayType类型。
<xs:complexType name="ArrayOfRecordType">
<xs:complexContent>
<xs:restriction base="soapenc:Array">
<xs:attribute ref="soapenc:arrayType" wsdl:arrayType="ax22:RecordType[]"></xs:attribute>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
如果对于Java客户端和服务端来说,这两种配置方式没有什么太大区别,但是如果对于.net来说,后者较好,可以产生ArrayOfRecordType的数组片断类型,手动解析数组内部信息。
首先ASF中集成的是Axis2的服务框架,因此出现这类问题要确定是集成中的定制导致SOAP消息出现了问题还是Axis2服务端本身解析的时候就有问题。搭建了基于Tomcat的axis2和xfire两个发布环境和ASF的服务发布环境,同时用SoapUI工具来截取消息返回包,根据返回的情况可以确认Axis2和ASF两者返回的SOAP消息一致,Xfire返回的消息为另外一种形式。
Xfire返回的SOAP消息包:
<soap:Envelope >http://schemas.xmlsoap.org/soap/envelope/" >http://www.w3.org/2001/XMLSchema" >http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<queryAccountRecordResponse >http://cmptest2.webservice.asf.xplatform.alisoft.com">
<out>
<name >http://cmptest.webservice.asf.xplatform.alisoft.com">1</name>
<recordArr >http://cmptest.webservice.asf.xplatform.alisoft.com">
<RecordType>
<accountBalance>12.12</accountBalance>
<accountId>1</accountId>
<isDeleted>false</isDeleted>
</RecordType>
<RecordType>
<accountBalance>12.12</accountBalance>
<accountId>1</accountId>
<isDeleted>false</isDeleted>
</RecordType>
</recordArr>
</out>
</queryAccountRecordResponse>
</soap:Body>
</soap:Envelope>
Axis2和ASF返回的消息包:
<soapenv:Envelope >http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:queryAccountRecordResponse >http://cmptest.webservice.asf.xplatform.alisoft.com">
<ns:return type="com.alisoft.xplatform.asf.webservice.cmptest.DemoStruct" >http://cmptest.webservice.asf.xplatform.alisoft.com/xsd">
<ax21:name>1</ax21:name>
<ax21:recordArr type="com.alisoft.xplatform.asf.webservice.cmptest.RecordType">
<ax21:accountBalance>12.12</ax21:accountBalance>
<ax21:accountId>1</ax21:accountId>
<ax21:isDeleted>false</ax21:isDeleted>
</ax21:recordArr>
<ax21:recordArr type="com.alisoft.xplatform.asf.webservice.cmptest.RecordType">
<ax21:accountBalance>12.12</ax21:accountBalance>
<ax21:accountId>1</ax21:accountId>
<ax21:isDeleted>false</ax21:isDeleted>
</ax21:recordArr>
</ns:return>
</ns:queryAccountRecordResponse>
</soapenv:Body>
</soapenv:Envelope>
根据报文可以很明显的看出,Axis2是将数组属性对象拆分为多个片断,并行放在SOAP中,每个片段的标签就是这个数组属性的名称。Xfire是将数组属性作为一个单独的片断,然后片断中包含了多个实体片断,片段的标签是实体对象名称。考虑一下wsdl中定义的情况,自己觉得Xfire是比较合理的解析方式,加上同时早期对于java 开发的ISV提倡使用xfire客户端,因此打算改造集成在ASF中Axis2的服务端和客户端。
问题的解决方案:
其实Axis2在做SOAP输出的时候主要经历了这么几部分,将返回的对象转变成为dom tree,然后将dom tree转换成为xml stream,最后转换成为soap消息包。因此只需要将对象转变成为dom tree时结构修正,那么就可以保证后续的工作正常。同样ASF客户端就是一个反向过程,只需要将dom tree转换成为对象改造一下就可以了。细节不做赘述。
问题的反复:
改造以后ASF框架已经能够返回Xfire可识别的消息结果,XFire问题解决。但是因为修改了比较多的内容,因此需要做全面的单元测试,结果一测试果然发现了问题,发现原来正常的接口返回数组对象情况不能正常。
截获SOAP消息以后发现如果返回数组类型原来的SOAP消息包如下:
<soap:Envelope >http://schemas.xmlsoap.org/soap/envelope/" >http://www.w3.org/2001/XMLSchema" >http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<getUserAccountArrResponse >http://webservice.asf.xplatform.alisoft.com">
<out>
<ns1:AccountBean >http://object.webservice.asf.xplatform.alisoft.com">
<accountBalance >http://object.webservice.asf.xplatform.alisoft.com">100.23</accountBalance>
<accountId >http://object.webservice.asf.xplatform.alisoft.com">1</accountId>
<isDeleted >http://object.webservice.asf.xplatform.alisoft.com">false</isDeleted>
</ns1:AccountBean>
<ns1:AccountBean >http://object.webservice.asf.xplatform.alisoft.com">
<accountBalance >http://object.webservice.asf.xplatform.alisoft.com">111.23</accountBalance>
<accountId >http://object.webservice.asf.xplatform.alisoft.com">11</accountId>
<isDeleted >http://object.webservice.asf.xplatform.alisoft.com">false</isDeleted>
</ns1:AccountBean>
</out>
</getUserAccountArrResponse>
</soap:Body> </soap:Envelope>
现在却在out后面多了一层ArrayOfAccountBean的标签,其实这就是刚才修改所造成的,而Xfire和axis2都不能够解析此类返回。
考虑了一下原因,其实如果数组作为结果返回的时候是不需要再多封装一层,而作为其他对象的属性的时候由于要复制给对象,因此需要将属性名称作为外围标签。所以还需要再次修改服务端,需要在数组封装返回的时候做判断,如果返回的数组是作为数组结果返回,那么不需要再次封装,如果是作为对象属性来返回,那么需要再次封装。
调整服务端以后作了全面的单元测试,一切正常。
后语:
现在由于SOA盛行,而Web Service作为SOA的技术解决手段被越来越广泛的使用,虽然SOAP消息和wsdl的标准都是规范性的定义,但是涉及到了细节实现,不同的框架开发者和不同语言的开发设计者都有不同的理解,稍有不同的理解偏差,就会导致同一种协议的不同客户端和服务端无法相互兼容,这类问题在通信行业中很常见,不同的终端手机的兼容性往往是增值业务开发者最头痛的问题。细节决定成败,跨平台的路还很长。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
事件驱动框架和SOA在空军的应用
空军正在利用SOA来改善数据共享,并实时跟踪战机,美国空军机动司令部的Michael Marek解释了企业可从中学习的经验。
-
揭秘New Relic APM技术细节
New Relic应性能管理(APM)套件主要用于Web软件开发。它允许用户在面向服务的架构(SOA)上跟踪关键事务性能,并且支持代码级别的可见性来评估特定代码段和SQL语句对性能的影响
-
仅凭SOA和云无法解决业务数据管理风险问题
SOA和云可以是某些恼人问题高效的解决方案;这一点我们已经知道了。但是也要记住它们并不是所有事情的直接答案,特别是当你的问题是业务数据管理风险,而不是技术问题时。
-
内存数据网格提供商一头扎进Java
10年的时间里,应用性能解决方案提供商Alachisoft一直在用NCache(针对N-Tier和网格计算.NET应用的内存计算和数据网格产品)为.NET社区服务。