12.6. 使用 WSDL 自省 SOAP Web 服务

像 Web 服务领域的许多事物一样,WSDL 历史悠久,充满政治斗争和阴谋。我将完全跳过这段历史,因为它让我感到非常无聊。还有其他标准试图做类似的事情,但 WSDL 赢了,所以让我们学习如何使用它。

WSDL 允许您做的最基本的事情是发现 SOAP 服务器提供的可用方法。

示例 12.8. 发现可用方法

>>> from SOAPpy import WSDL          1
>>> wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl')
>>> server = WSDL.Proxy(wsdlFile)    2
>>> server.methods.keys()            3
[u'getTemp']
1 SOAPpy 包含一个 WSDL 解析器。在撰写本文时,它被标记为处于开发的早期阶段,但我解析尝试过的任何 WSDL 文件都没有问题。
2 要使用 WSDL 文件,您再次使用代理类 WSDL.Proxy,它接受一个参数:WSDL 文件。请注意,在这种情况下,您传入的是存储在远程服务器上的 WSDL 文件的 URL,但代理类也适用于 WSDL 文件的本地副本。创建 WSDL 代理的行为将下载 WSDL 文件并解析它,因此如果 WSDL 文件中存在任何错误(或者由于网络问题而无法获取),您将立即知道。
3 WSDL 代理类将可用函数公开为 Python 字典 server.methods。因此,获取可用方法列表就像调用字典方法 keys() 一样简单。

好的,所以您知道此 SOAP 服务器提供了一种方法:getTemp。但是你如何调用它?WSDL 代理对象也可以告诉您。

示例 12.9. 发现方法的参数

>>> callInfo = server.methods['getTemp']  1
>>> callInfo.inparams                     2
[<SOAPpy.wstools.WSDLTools.ParameterInfo instance at 0x00CF3AD0>]
>>> callInfo.inparams[0].name             3
u'zipcode'
>>> callInfo.inparams[0].type             4
(u'http://www.w3.org/2001/XMLSchema', u'string')
1 server.methods 字典中填充了一个名为 CallInfoSOAPpy 特定结构。CallInfo 对象包含有关一个特定函数的信息,包括函数参数。
2 函数参数存储在 callInfo.inparams 中,它是 Python 列表,其中包含保存每个参数信息的 ParameterInfo 对象。
3 每个 ParameterInfo 对象都包含一个 name 属性,它是参数名称。您不需要知道参数名称就可以通过 SOAP 调用函数,但 SOAP 确实支持使用命名参数调用函数(就像 Python 一样),并且如果您选择使用它们,WSDL.Proxy 将正确处理将命名参数映射到远程函数。
4 每个参数也使用 XML Schema 中定义的数据类型进行显式类型化。您在上一节的线路跟踪中看到了这一点;XML Schema 命名空间是我告诉您忽略的“样板文件”的一部分。就我们这里的目的而言,您可以继续忽略它。zipcode 参数是一个字符串,如果您将 Python 字符串传递给 WSDL.Proxy 对象,它将正确映射它并将其发送到服务器。

WSDL 还允许您自省函数的返回值。

示例 12.10. 发现方法的返回值

>>> callInfo.outparams            1
[<SOAPpy.wstools.WSDLTools.ParameterInfo instance at 0x00CF3AF8>]
>>> callInfo.outparams[0].name    2
u'return'
>>> callInfo.outparams[0].type
(u'http://www.w3.org/2001/XMLSchema', u'float')
1 函数参数的 callInfo.inparams 的附属物是返回值的 callInfo.outparams。它也是一个列表,因为通过 SOAP 调用的函数可以返回多个值,就像 Python 函数一样。
2 每个 ParameterInfo 对象都包含 nametype。此函数返回一个名为 return 的值,它是一个浮点数。

让我们把它们放在一起,并通过 WSDL 代理调用 SOAP Web 服务。

示例 12.11. 通过 WSDL 代理调用 Web 服务

>>> from SOAPpy import WSDL
>>> wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl')
>>> server = WSDL.Proxy(wsdlFile)               1
>>> server.getTemp('90210')                     2
66.0
>>> server.soapproxy.config.dumpSOAPOut = 1     3
>>> server.soapproxy.config.dumpSOAPIn = 1
>>> temperature = server.getTemp('90210')
*** Outgoing SOAP ******************************************************
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getTemp xmlns:ns1="urn:xmethods-Temperature" SOAP-ENC:root="1">
<v1 xsi:type="xsd:string">90210</v1>
</ns1:getTemp>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
************************************************************************
*** Incoming SOAP ******************************************************
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<ns1:getTempResponse xmlns:ns1="urn:xmethods-Temperature"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:float">66.0</return>
</ns1:getTempResponse>

</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
************************************************************************

>>> temperature
66.0
1 配置比直接调用 SOAP 服务更简单,因为 WSDL 文件包含调用服务所需的服务 URL 和命名空间。创建 WSDL.Proxy 对象将下载 WSDL 文件,解析它,并配置一个 SOAPProxy 对象,用于调用实际的 SOAP Web 服务。
2 创建 WSDL.Proxy 对象后,您可以像使用 SOAPProxy 对象一样轻松地调用函数。这并不奇怪;WSDL.Proxy 只是 SOAPProxy 的一个包装器,添加了一些自省方法,因此调用函数的语法是相同的。
3 您可以使用 server.soapproxy 访问 WSDL.ProxySOAPProxy。这对于打开调试非常有用,因此当您可以通过 WSDL 代理调用函数时,它的 SOAPProxy 将转储通过网络传输的传出和传入 XML 文档。