12.6. Introspección de servicios web SOAP con WSDL
Como muchas otras cosas en el campo de los servicios web, WSDL
tiene una historia larga y veleidosa, llena de conflictos e intrigas
políticas. Me saltaré toda esta historia ya que me aburre hasta lo
indecible. Hay otros estándares que intentaron hacer cosas similares, pero
acabó ganando WSDL así que aprendamos cómo usarlo.
Lo más fundamental que nos permite hacer WSDL es descubrir los
métodos que nos ofrece un servidor SOAP.
Ejemplo 12.8. Descubrimiento de los métodos disponibles
>>> from SOAPpy import WSDL
>>> wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl'
>>> server = WSDL.Proxy(wsdlFile)
>>> server.methods.keys()
[u'getTemp']
|
SOAPpy incluye un analizador de WSDL. En el momento en que
escribo esto se indicaba que estaba en las fases tempranas de desarrollo,
pero no he tenido problemas con ninguno de los ficheros WSDL que
he probado.
|
|
Para usar un fichero WSDL debemos de nuevo utilizar una clase
proxy, WSDL.Proxy, que toma un único argumento: el
fichero WSDL. Observe que en este caso estamos pasando la URL de un
fichero WSDL almacenado en un servidor remoto, pero las clases proxy
trabajan igual de bien con una copia local del fichero WSDL. El acto de
crear el proxy WSDL descargará el fichero y lo analizará, así que si
hubiera algún error en el fichero WSDL (o si no pudiera acceder a él
debido a problemas de red) lo sabría de inmediato.
|
|
La clase proxy WSDL expone las funciones disponibles como un
diccionario de Python, server.methods. Así que
obtener la lista de métodos disponibles es tan simple como invocar el
método keys() del diccionario.
|
Bien, así que sabemos que este servidor SOAP ofrece un solo
método: getTemp. Pero, ¿cómo lo invocamos? El
objeto proxy WSDL también nos puede decir eso.
Ejemplo 12.9. Descubrimiento de los argumentos de un método
>>> callInfo = server.methods['getTemp']
>>> callInfo.inparams
[<SOAPpy.wstools.WSDLTools.ParameterInfo instance at 0x00CF3AD0>]
>>> callInfo.inparams[0].name
u'zipcode'
>>> callInfo.inparams[0].type
(u'http://www.w3.org/2001/XMLSchema', u'string')
|
El diccionario server.methods contiene una
estructura específica de SOAPpy llamada CallInfo.
Un objeto CallInfo lleva información sobre una
función específica, incluyendo sus argumentos.
|
|
Los argumentos de la función están almacenados en
callInfo.inparams, que es una lista de objetos
ParameterInfo que contienen información sobre cada
parámetro.
|
|
Cada objeto ParameterInfo contiene un
atributo name que es el nombre del argumento. No hace
falta saber el nombre del argumento para llamar a la función mediante
SOAP, pero SOAP admite invocación de funciones con argumentos por
nombre (igual que Python), y WSDL.Proxy
relacionará correctamente los argumentos nombrados con la función remota
si escoge usarlos.
|
|
También se da el tipo explícito de cada parámetro, usando tipos
definidos en XML Schema. Ya vio esto en la traza de datos de la sección
anterior; el espacio de nombres de XML Schema era parte de la
“plantilla” que le pedí que ignorase. Puede continuar
ignorándolos para nuestros propósitos actuales. El parámetro
zipcode es una cadena de texto y si pasa una cadena de
Python al objeto WSDL.Proxy, lo convertirá
correctamente para enviarlo al servidor.
|
WSDL también le permite hacer introspección sobre los valores de
vuelta de una función.
Ejemplo 12.10. Descubrimiento de los valores de retorno de un método
>>> callInfo.outparams
[<SOAPpy.wstools.WSDLTools.ParameterInfo instance at 0x00CF3AF8>]
>>> callInfo.outparams[0].name
u'return'
>>> callInfo.outparams[0].type
(u'http://www.w3.org/2001/XMLSchema', u'float')
|
El adjunto de callInfo.inparams para los
argumentos de las funciones es callInfo.outparams para
sus valores de retorno. También es una lista, porque las funciones
invocadas mediante SOAP pueden devolver varios valores, igual que las
funcionesde Python.
|
|
Cada objeto ParameterInfo contiene
nombre (name) y tipo (type). Esta
función devuelve un solo valor, de nombre return, que
es de coma flotante.
|
Juntémoslo todo y llamemos a un servicio web SOAP mediante un
proxy WSDL.
Ejemplo 12.11. Invocación de un servicio web mediante un proxy WSDL
>>> from SOAPpy import WSDL
>>> wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl')
>>> server = WSDL.Proxy(wsdlFile)
>>> server.getTemp('90210')
66.0
>>> server.soapproxy.config.dumpSOAPOut = 1
>>> 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
|
La configuración es más sencilla que al llamar directamente al
servicio SOAP, ya que el fichero WSDL contiene tanto la URL como el
espacio de nombres que necesitamos para llamar al servicio. La creación
del objeto WSDL.Proxy descarga el fichero WSDL,
lo analiza y configura un objeto SOAPProxy que usa
para invocar al servicio web SOAP.
|
|
Una vez se crea el objeto WSDL.Proxy
invocamos a la función tan fácilmente como lo haríamos con el objeto
SOAPProxy. Esto no es sorprendente; el
WSDL.Proxy es un simple envoltorio alrededor de
SOAPProxy con algunos métodos de introspección
añadidos, así que la sintaxis de llamada de funciones es la misma.
|
|
Podemos acceder al SOAPProxy de
WSDL.Proxy con server.soapproxy.
Esto es útil para activar la depuración, para que cuando invoquemos
funciones mediante el proxy WSDL su SOAPProxy
vuelque los documentos XML de salida y entrada.
|