12.6. Introspection de services Web SOAP avec WSDL

Comme beaucoup de choses dans le domaine des services Web, WSDL a un longue et tortueuse histoire, pleine de controverses politiques et d'intrigue. Je n'aborderais pas du tout cette histoire, je la trouve ennuyeuse à pleurer. Il y a eu des normes concurrentes pour remplir ces fonctions, mais WSDL a gagné, donc apprenons à l'utiliser.

La chose la plus importante que WSDL permet de faire est la découverte des méthodes offertes par un serveur SOAP.

Exemple 12.8. Découverte des méthodes disponibles

>>> 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 intègre un analyseur WSDL. Au moment où j'écris ces lignes, il est considéré comme aux premiers stades de son développement, mais je n'ai eu aucun problème pour analyser les fichiers WSDL que j'ai testé.
2 Pour utiliser un fichier WSDL, nous utilisons à nouveau une classe de délégation, WSDL.Proxy, qui prend un seul argument : le fichier WSDL. Notez qu'ici nous lui passons l'URL d'un fichier WSDL situé sur le serveur distant, mais la classe de délégation marche tout aussi bien avec une copie locale du fichier WSDL. La création de la classe déclenchera le téléchargement et l'analyse du fichier WSDL, donc si il contient des erreurs (ou s'il est inaccessible à cause de problèmes de réseau) nous l'apprenons immédiatement.
3 La classe de délégation WSDL expose les fonctions disponibles sous la forme d'un dictionnaire Python, server.methods. Obtenir la liste des méthodes disponibles consiste donc simplement à appeler la méthode de dictionnaire keys().

Donc, nous savons que le serveur SOAP offre un méthode unique : getTemp. Mais comment l'appeler ? L'objet de délégation WSDL peut nous l'indiquer.

Exemple 12.9. Découverte des arguments d'une méthode

>>> 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 Le dictionnaire server.methods contient une structure spécifique à SOAPpy appelée CallInfo. Un objet CallInfo contient des informations au sujet d'une fonction spécifique, y compris ses arguments.
2 Les arguments de la fonction sont stockés dans callInfo.inparams, qui est une liste Python d'objets ParameterInfo qui contiennent des informations sur chaque paramètre.
3 Chaque objet ParameterInfo contient un attribut name, qui est le nom de l'argument. Il n'est pas nécessaire de connaître le nom de l'argument pour appeler la fonction par SOAP, mais SOAP permet l'appel de fonction avec des arguments nommés (tout comme Python) et WSDL.Proxy fera la correspondance entre les arguments nommés et la fonction distante si ils sont utilisés.
4 Chaque paramètre est explicitement typé, les types de données étant définis par XML Schema. Nous l'avons vu dans la sortie de la section précédente, l'espace de noms XML Schema faisait partie du «code administratif» que je vous avais dit d'ignorer et vous pouvez continuer de l'ignorer. Le paramètre zipcode est une chaîne et si vous passez une chaîne Python à l'objet WSDL.Proxy il l'enverra au serveur.

WSDL permet également de connaître par introspection les valeurs de retour d'une fonction.

Exemple 12.10. Découverte des valeurs de retour d'une fonction

>>> 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 L'équivalent de callInfo.inparams pour les valeurs de retour est callInfo.outparams. C'est aussi une liste, car les fonctions appelées par SOAP peuvent retourner des valeurs multiples, tout comme les fonctions Python.
2 Chaque objet ParameterInfo contient des variables name et type. Ici, la fonction retourne une seule valeur, appelée return et qui est un nombre à virgule flottante.

Assemblons tout cela et appelons un service Web SOAP avec un objet de délégation WSDL

Exemple 12.11. Appel d'un service Web avec un objet de délégation WSDL

>>> 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 La configuration est plus simple que pour appeler un service SOAP directement, car le fichier WSDL contient à la fois l'URL et l'espace de noms dont nous avons besoin pour appeler le service. La création de l'objet WSDL.Proxy entraîne le téléchargement et l'analyse du fichier WSDL et la configuration de l'objet SOAPProxy utilisé pour appelé le service Web SOAP
2 Une fois l'objet WSDL.Proxy créé, nous pouvons appeler une fonction aussi simplement que nous l'avions fait avec l'objet SOAPProxy. Cela n'est pas surprenant, WSDL.Proxy n'est qu'une enveloppe autour de SOAPProxy avec quelques méthodes d'introspection en plus, la syntaxe d'appel de fonctions est donc la même.
3 Nous pouvons accéder à l'objet SOAPProxy de WSDL.Proxy par server.soapproxy. C'est utile pour activer le débogage, pour que l'objet SOAPProxy de l'objet de délégation WSDL affiche les documents XML en sortie et en entrée.