在本专栏的最近一篇 ZSI 文章发布时,它已经是 1.2 版本了。 经过 Rich Salz 所领导的开发人员的努力,ZSI 发生了一些改变。目前它已经是 1.4.1 版本,添加了一些 WSDL 支持。 Uche Ogbuji 和 Scott Archer 在这里讲述了这些新特性,并且展示了 ZSI 的第三方包装器。
“Web Services for Python” 项目构成了 ZSI 和 SOAPpy 的保护伞,它提供了具有良好可维护性和高度可用性的 Web 服务工具。在本专栏前面的几篇文章中我们已经清楚地了解了这些包:
在我们撰写了这些文章之后,这些包已经发生了重大的改进并且添加了一些特征。在本文中,我们将先看看 ZSI 中的这些新特征,以了解如何通过这些特征来获得具有更高的工业强度的公共 Web 服务。
我们先看一下 ZSI 1.4.1,请在 Python 2.2.2 中对其进行测试。ZSI 中的最大更新是对 WSDL 的支持。其中有一个新的 ZSI 类 ServiceProxy
,您可以使用 WSDL 文件的 URL 来初始化它。文档指出:“使用 WSDL
实例来发送和接收消息”。如果在这个专栏中您遵循了 ZSI 的规范,您将记得 ZSI 是一种优秀的 Web 服务库,它遵循了某些更为严格的规则,但遗憾的是,需要做一些不确定的改动(并且您需要编写的代码可能比您想像的要多得多)才能让它处理常见的情形。
的确,ZSI 看起来可以处理任何 Web 服务。只不过,您可能会觉得宁可它不出现太多的变化,自己也不愿意编写各种各样的复杂代码来处理带有 类型代码(type codes)的结构。产生这种问题的根源在于 SOAP 消息结构试图结合 RPC 传统的精确性以及 XML 的灵活性。WSDL 的目标之一是提供充分的关于服务的消息协议的结构化描述,这样它就允许应用程序能够自动解决开发人员需要处理的问题。在 ZSI 中添加 WSDL 支持,使得开发人员不用关注 SOAP 消息的解释。不管 Web 服务是否使用位置参数、命名参数或者两者,也不管它是否使用简单的、复杂的或者自定义的数据类型,WSDL 都应该提供所需的信息。因而,正如您所期望的,我们不是在以前用过 ZSI 的玩具 Web 服务(比如 Captain Haddock Curser 或简单日历服务)上尝试 ServiceProxy
,而是在终于开始出现的基于 SOAP 的公共 Web 服务上尝试 ServiceProxy
。
Richard Hastings 的 Air Fare Quote Search 是用 Apache Axis 实现的,可以用来搜索一些实时的航班 Web 站点,以查找特定航线最合适的机票价格(请参见 参考资料)。它收集并返回按照价格分类的结果。WSDL 位于 http://wavendon.dsdata.co.uk:8080/axis/services/SBGGetAirFareQuote?wsdl。它定义了两个操作: getAirFareQuote
和 getAirlines
。前者用于执行价格搜索,使用 4 个参数:两个 W3C XML Schema Language Data Types(WXSDT) dateTime
值,给出了航班出发以及到达的大概时间,两个 WXSDT string
值,给出了三个字母组成的飞机往返机场的代码。在交互式的 Python 中,我们尝试了如下代码:
>>> from ZSI import ServiceProxy >>> wsdl = 'http://wavendon.dsdata.co.uk:8080// ... /axis/services/SBGGetAirFareQuote?wsdl' >>> proxy = ServiceProxy(wsdl) |
ZSI 手册中的示例也展示了如何将 typesmodule
关键字参数用于 ServiceProxy
初始化器,但是还没有适当地解释这个参数,所以我们忽略它。
我们需要将两个 date/time 对象用于服务调用,而且 ZSI 手册指出:“SOAP 日期和时间是用 UTC(GMT)表示的 Python 时间元组,与 Python 时间模块中所记录的一样”。我们尝试过用以下代码来查看参加在费城举行的 XML 2003 大会的花费是多少。
>>> import time >>> ISO_8601_DATETIME = '%Y-%m-%dT%H:%M:%S' >>> dep = time.strptime('2003-12-06T12:30:59', ISO_8601_DATETIME) >>> ret = time.strptime('2003-12-12T12:30:59', ISO_8601_DATETIME) >>> proxy.getAirFareQuote(dep, ret, 'den', 'phl') Traceback (most recent call last): File "<stdin>", line 1, in ? File "/usr/lib/python2.2/site-packages/ZSI/ServiceProxy.py", line 82, in __call__ return self.parent()._call(self.__name__, *args, **kwargs) File "/usr/lib/python2.2/site-packages/ZSI/ServiceProxy.py", line 65, in _call apply(getattr(binding, callinfo.methodName), args) File "/usr/lib/python2.2/site-packages/ZSI/client.py", line 28, in __call__ requesttypecode=TC.Any(self.name, aslist=1)) File "/usr/lib/python2.2/site-packages/ZSI/client.py", line 131, in RPC self.Send(url, opname, obj, **kw) File "/usr/lib/python2.2/site-packages/ZSI/client.py", line 169, in Send sw.serialize(obj, tc, typed=0) File "/usr/lib/python2.2/site-packages/ZSI/writer.py", line 73, in serialize typecode.serialize(self, pyobj, **kw) File "/usr/lib/python2.2/site-packages/ZSI/TC.py", line 282, in serialize Any().serialize(sw, val) File "/usr/lib/python2.2/site-packages/ZSI/TC.py", line 315, in serialize raise EvaluateException('''Any can't serialize ''' + // ZSI.EvaluateException: Any can't serialize (2003, 12, 6, 12, 30, 59, 5, 340, 0) |
我们做过许多徒劳无功的试验(其中包括使用 ZSI 的 gDateTime
类),钻研过文档,甚至用 Google 多次搜索该主题,都无法解决如何使 ZSI 以参数的形式传递 date/time,而不用为使用 gDateTime
类作为 类型代码的 codedate/time 对象编写特定的类。这看起来是开发人员无法逾越的一个障碍,他们无法编写这样一个通用的数据类型。
|
ZSI 现在也带有一种脚本,也就是 wsdl2py
,在文档中没有记录它,不过 Python 源代码附带了这样的文档字符串:
“一个自动从 wsdl 定义生成客户端接口代码的实用程序和一组代表元素声明和类型定义的类。这将在当前的工作目录下生成两个文件,该目录以 wsdl 定义名称来命名。”
这看起来,我们可以看到在 WSDL 文件中定义的 WXSDT dateTime
参数,生成所有的用于从 Python 编组这些值的工具。这些脚本不能处理 WSDL URL,所以我们必须把这些定义下载到本地文件。下例显示了如何在 UNIX 上使用 wget
命令:
这听起来像是很有希望的,并且我们认为有可能在 WSDL 文件中定义 WXSDT dateTime
参数,并且生成需要用来解组 Python 中的这些值的工具。该脚本不能处理 WSDL URL,所以我们必须把定义下载到本地文件。下面是一个如何在 UNIX 上使用 wget
命令的示例:
wget -O SBGGetAirFareQuote.wsdl // http://wavendon.dsdata.co.uk:8080/axis/services/SBGGetAirFareQuote?wsdl |
遗憾的是,使用 wsdl2py
脚本的作用是非常有限的,如下面的清单所示:
wsdl2py -f SBGGetAirFareQuote.wsdl Traceback (most recent call last): ... Part of traceback omitted for brevity ... File "/usr/lib/python2.2/site-packages/ZSI/wsdl2python.py", line 1058, in _fromComplexType self._complexTypeComplexContent(tp) File "/usr/lib/python2.2/site-packages/ZSI/wsdl2python.py", line 1147, in _complexTypeComplexContent arrayinfo = dt.getArrayType() File "/usr/lib/python2.2/site-packages/ZSI/wsdlInterface.py", line 1712, in getArrayType raise WsdlInterfaceError, 'could not determine array type' ZSI.wsdlInterface.WsdlInterfaceError: could not determine array type |
看来该脚本不能处理 getAirFareQuote
操作的搜索返回结构中的复杂类型数组,如 WSDL 类型部分所示:
<complexType name="ArrayOfAirFareQuote"> <complexContent> <restriction base="soapenc:Array"> <attribute wsdl:arrayType="impl:AirFareQuote[]" ref= "soapenc:arrayType"/> </restriction> </complexContent> </complexType> <element nillable="true" type="impl:ArrayOfAirFareQuote" name= "ArrayOfAirFareQuote"/> <complexType name="ArrayOf_xsd_string"> <complexContent> <restriction base="soapenc:Array"> <attribute wsdl:arrayType="xsd:string[]" ref= "soapenc:arrayType"/> </restriction> </complexContent> </complexType> <element nillable="true" type="impl:ArrayOf_xsd_string" name= "ArrayOf_xsd_string"/> |
WSDL 数组类型或许是在 XML 中缺少编码 RPC 有效负载方面最著名的一个例子,这对于 ZSI 来说是自然而然的事情,不过,我们将在另一篇文章中对此进行讨论。注释掉上面的类型定义,而代之以伪 WXSDT string
类型 wsdl2py
, wsdl2py
继而生成用看起来很显眼的代码包装的两个模块。不幸的是,查看这些代码并不能启发如何正确地编组输入数据参数,所以我们很遗憾地把它看作是一个失败。为了让我们从责骂中解脱出来,我们询问了一名开发人员,了解编组数据类型的困难,他的答复是这可能是 ZSI 中的一个缺陷,但是可望很快修复。如果想更多地了解这个问题,可以看看本专栏的论坛中的帖子(请参见 参考资料)。
|
一个尝试使用 ZSI 的开发人员至少会认为必须有一种便利的方法来利用丰富的底层 ZSI 接口。OSE 是一个分布式的、基于 Web 的应用程序的框架,它提供构建在 ZSI 之上的 SOAP 支持。 zsirpc 包是 OSE 的一部分,它提供了一个更友好的 ZSI 包装器,不过它只处理用类似于 XML-RPC 的方式编码的 Web 服务。最有意义的是,它为 ZSI 支持的布尔型、二进制(如用 BASE64 编码的)、日期、dateTime、时间和持久类型提供了直接的包装器,只不过,据我们所知,为了编写专门的结构,开发人员需要做许多额外的工作。
我们希望 zsirpc 中的一些代码可以放到 ZSI 本身中。这将给 RPC 样式的 Web 服务开发人员带来更多的便利。然而,也需要鼓励开发人员使用文档-文字样式的 Web 服务。我们提倡这种样式的 Web 服务已经有很长的时间了,而最好的结果是看到 Web 服务的主流最终能够朝着这个方向发展。从而允许开发人员用动态 XML 本身来表示消息,而不是一味地想方设法扩展单一的数据模型来进行跨平台和语言的通信,这样一不留神,就会陷入转换层的重重迷雾之中。
ZSI 不是惟一取得重大进展的 Python Web 服务库。SOAPpy 也在积极的开发之中,而 WSDL 同样是这项工作的重要组成部分。在不久的将来我们将看到 SOAPpy 中有哪些新的特征。
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
- 请单击文章上方或下方的 讨论参加有关本文的 讨论论坛。
- 在 SourceForge 中查阅 Python Web Services,它是托管 ZSI 和 SOAPpy 的项目。
- 参看 Richard Hastings 的 Air Fare Quote Search,它是用 Apache Axis 实现的,可以搜索一些实时的航班 Web 站点,以查找特定航线最合适的机票价格。
- OSE是一个分布式应用程序框架项目,zsirpc 是其中的一个组件。 zsirpc 可以从 下载中作为一个单独的包下载。还有一个基于 Web 的 SOAP Debugger,您可以通过它体验一下 zsirpc 。
- 在 James McCarthy 撰写的这篇文章 "Reap the benefits of document style Web services"中讨论了文档/文字样式的 SOAP 应用( developerWorks,2002 年 6 月)。
- 从 通过 SMTP 处理 SOAP阅读本系列前面的几篇文章,或者查阅 Python Web 服务开发者专栏。
Scott Archer 是一名软件架构设计师和 GlowingOrb Inc. 的共同创始人,该公司是一家软件工具开发商并专门提供模型驱动的解决方案和将这些解决方案集成到核心业务流程的软件。 Archer 从香港大学的计算分子生物学(Computational Molecular Biology)获得了哲学硕士(M.Phil)学位。您可以通过 scott.archer@glowingorb.com 与他联系。 |
Uche Ogbuji 是 Fourthought 公司的顾问和合伙创始人,该公司是一家软件供应商并专门提供企业知识管理应用程序的 XML 解决方案方面的咨询。Fourthought 开发的 4Suite是供 XML 中间件使用的开放源码平台。Ogbuji 是一名计算机工程师和作家,他出生在尼日尼亚于,现生活和工作在美国科罗拉多州的玻尔得市。您可以通 uche.ogbuji@fourthought.com 与他联系。 |