您的位置:首页 > 其它

由SOAP消息看Web Service FrameWork的细节差异

2008-01-31 00:06 471 查看
早先的一些blog文章已经说了很多关于Web Service客户端在同一种语言实现或者不同语言实现中由于细节实现不同导致兼容性的问题。昨天解决的问题在一个边界问题上又一次说明了这种异构环境的互通标准在不同的实现当中,由于细节理解不同会造成一些兼容性问题。

问题:
当接口返回类型为一个对象,同时这个对象中有一个属性是数组类型,那么这个数组类型的对象在返回的SOAP消息中就无法正确解析。如下定义了一个对象:
[align=left]publicclass DemoStruct implements Serializable[/align]
[align=left]{[/align]
[align=left] private RecordType[] recordArr;[/align]
[align=left] private String name;[/align]
[align=left] public RecordType[] getRecordArr(){returnrecordArr;}[/align]
[align=left] publicvoid setRecordArr(RecordType[] recordArr){this.recordArr = recordArr;}[/align]
[align=left] public String getName(){returnname;}[/align]
[align=left] publicvoid setName(String name){this.name = name;} [/align]
}
接口定义如下:
[align=left]publicinterface IDemoTest[/align]
[align=left]{[/align]
[align=left] DemoStruct queryAccountRecord(String account);[/align]
}

问题分析:
在早先返回数组对象的问题上已经解决了,但是当数组归属于对象,作为一个属性的时候,出现了问题。
首先在wsdl中定义一个类型是数组有两种方式:
1. 定义maxoccurs=”unbounded”。
[align=left]<xsd:complexType name="ArrayOfRecordType">[/align]
[align=left]<xsd:sequence>[/align]
[align=left]<xsd:element maxOccurs="unbounded" minOccurs="0" name="RecordType" nillable="true" type="ns1:RecordType"/>[/align]
[align=left]</xsd:sequence>[/align]
</xsd:complexType>
2. 定义soap encoding中的arrayType类型。
[align=left]<xs:complexType name="ArrayOfRecordType">[/align]
[align=left] <xs:complexContent>[/align]
[align=left] <xs:restriction base="soapenc:Array">[/align]
[align=left] <xs:attribute ref="soapenc:arrayType" wsdl:arrayType="ax22:RecordType[]"></xs:attribute>[/align]
[align=left] </xs:restriction>[/align]
[align=left] </xs:complexContent>[/align]
</xs:complexType>

如果对于Java客户端和服务端来说,这两种配置方式没有什么太大区别,但是如果对于.net来说,后者较好,可以产生ArrayOfRecordType的数组片断类型,手动解析数组内部信息。

首先ASF中集成的是Axis2的服务框架,因此出现这类问题要确定是集成中的定制导致SOAP消息出现了问题还是Axis2服务端本身解析的时候就有问题。搭建了基于Tomcat的axis2和xfire两个发布环境和ASF的服务发布环境,同时用SoapUI工具来截取消息返回包,根据返回的情况可以确认Axis2和ASF两者返回的SOAP消息一致,Xfire返回的消息为另外一种形式。
Xfire返回的SOAP消息包:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<queryAccountRecordResponse xmlns="http://cmptest2.webservice.asf.xplatform.alisoft.com">
<out>
<name xmlns="http://cmptest.webservice.asf.xplatform.alisoft.com">1</name>
<recordArr xmlns="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 xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:queryAccountRecordResponse xmlns:ns="http://cmptest.webservice.asf.xplatform.alisoft.com">
<ns:return type="com.alisoft.xplatform.asf.webservice.cmptest.DemoStruct" xmlns:ax21="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 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<getUserAccountArrResponse xmlns="http://webservice.asf.xplatform.alisoft.com">
<out>
<ns1:AccountBean xmlns:ns1="http://object.webservice.asf.xplatform.alisoft.com">
<accountBalance xmlns="http://object.webservice.asf.xplatform.alisoft.com">100.23</accountBalance>
<accountId xmlns="http://object.webservice.asf.xplatform.alisoft.com">1</accountId>
<isDeleted xmlns="http://object.webservice.asf.xplatform.alisoft.com">false</isDeleted>
</ns1:AccountBean>
<ns1:AccountBean xmlns:ns1="http://object.webservice.asf.xplatform.alisoft.com">
<accountBalance xmlns="http://object.webservice.asf.xplatform.alisoft.com">111.23</accountBalance>
<accountId xmlns="http://object.webservice.asf.xplatform.alisoft.com">11</accountId>
<isDeleted xmlns="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的标准都是规范性的定义,但是涉及到了细节实现,不同的框架开发者和不同语言的开发设计者都有不同的理解,稍有不同的理解偏差,就会导致同一种协议的不同客户端和服务端无法相互兼容,这类问题在通信行业中很常见,不同的终端手机的兼容性往往是增值业务开发者最头痛的问题。细节决定成败,跨平台的路还很长。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: