153 lines
6.7 KiB
Markdown
153 lines
6.7 KiB
Markdown
---
|
||
layout: post
|
||
title: Appel de WebServices Soap en Ruby
|
||
---
|
||
|
||

|
||
|
||
[Soap](http://fr.wikipedia.org/wiki/SOAP), on aime ou on aime pas. Je pense que Soap sur [HTTP](http://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol), c’est dommage, autant utiliser le protocole http correctement et proposer des services web en [RESTful](http://fr.wikipedia.org/wiki/Representational_State_Transfer) . Comme je l’ai lu quelque part, c’est comme “mettre une enveloppe dans une enveloppe”. Toujours est-il que ça a quand même le mérite d’exister . Et nous nous sommes retrouvé à devoir faire appel à un webservice SOAP en ruby, j’aimerais vous faire part de la façon dont on a réalisé cela.
|
||
|
||
Après avoir jeté un coup d’oeil à [soap4r](http://dev.ctor.org/soap4r) nous avons voulu regarder ce qui existait déjà, et là, surprise, soap4r est intégré à la [librairie standard de Ruby](http://ruby-doc.org/stdlib/) avec une base très riche pour l’utilisation de [Soap](http://ruby-doc.org/stdlib/libdoc/soap/rdoc/index.html) et quelques outils bien pratique pour l’utilisation des fichiers [WSDL](http://ruby-doc.org/stdlib/libdoc/wsdl/rdoc/index.html).
|
||
|
||
Du coup, il ne reste plus qu’a générer les classes qui s’occuperont de l’appel “soap” à l’aide de l’objet [WSDL2Ruby](http://ruby-doc.org/stdlib/libdoc/wsdl/rdoc/classes/WSDL/SOAP/WSDL2Ruby.html).
|
||
|
||
Pour cette génération, nous avons repris un script [wsdl2ruby.rb](http://dev.ctor.org/soap4r/browser/trunk/bin/wsdl2ruby.rb) trouvé dans les repertoires du Trac du projet soap4r (qui date un peu). En gros il ajoute quelque explication sur l’utilisation de la ligne de commande permettant de générer les classes Soap, et permet d’enchainer l’envoie du message `run` après avoir mis en place la `location` et placé quelque options bien choisi.
|
||
|
||
### Voici *LA* méthode principale du `wsdl2ruby.rb`:
|
||
|
||
<pre>
|
||
{% highlight ruby %}
|
||
def run
|
||
@worker = WSDL::SOAP::WSDL2Ruby.new
|
||
@worker.logger = @log
|
||
location, opt = parse_opt(GetoptLong.new(*OptSet))
|
||
usage_exit unless location
|
||
@worker.location = location
|
||
if opt['quiet']
|
||
self.level = Logger::FATAL
|
||
else
|
||
self.level = Logger::INFO
|
||
end
|
||
@worker.opt.update(opt)
|
||
@worker.run
|
||
0
|
||
end
|
||
{% endhighlight %}
|
||
</pre>
|
||
|
||
|
||
En executant donc cette commande `wsdl2ruby.rb --wsdl '[l'adresse du fichier wsdl de mon service soap
|
||
qui tue]' --type client`
|
||
|
||
Il existe pas mal d’option pour créer un webservice, mais ici nous voulions créer un client pour appeler un webservice soap existant.
|
||
|
||
|
||
Cette commande nous a donc générer deux fichiers: `default.rb` et `defaultDriver.rb`. Ce dernier s’occuper de la connexion au service, et défini les methodes que l’on peut utiliser ainsi que les paramètres qui vont bien avec. Le fichier `default` contient lui un objet par methode du service. Dans notre cas, nous avion un objet qui correspond à l’appel, et un pour la réponse. Bien sur, avant d’intégrer ce code généré, il faudrais faire un petit renomage, mais ça vous savez faire.
|
||
|
||
|
||
Notre service permet
|
||
|
||
### Le defaultDriver.rb généré.
|
||
|
||
<pre>
|
||
{% highlight ruby %}
|
||
require 'default.rb'
|
||
require 'soap/rpc/driver'
|
||
|
||
class Authentification < ::SOAP::RPC::Driver
|
||
DefaultEndpointUrl = "http://example.com:8080/axis/services/authentification"
|
||
MappingRegistry = ::SOAP::Mapping::Registry.new
|
||
|
||
Methods = [
|
||
[ "",
|
||
"authentification",
|
||
[ ["in", "parameters", ["::SOAP::SOAPElement", "http://auth.example.com/", "authentification"], true],
|
||
["out", "parameters", ["::SOAP::SOAPElement", "http://auth.example.com/", "response"], true] ],
|
||
{ :request_style => :document, :request_use => :literal, :response_style => :document, :response_use => :literal }
|
||
]
|
||
]
|
||
|
||
def initialize(endpoint_url = nil)
|
||
endpoint_url ||= DefaultEndpointUrl
|
||
super(endpoint_url, nil)
|
||
self.mapping_registry = MappingRegistry
|
||
init_methods
|
||
end
|
||
|
||
private
|
||
|
||
def init_methods
|
||
Methods.each do |definitions|
|
||
opt = definitions.last
|
||
if opt[:request_style] == :document
|
||
add_document_operation(*definitions)
|
||
else
|
||
add_rpc_operation(*definitions)
|
||
qname = definitions[0]
|
||
name = definitions[2]
|
||
if qname.name != name and qname.name.capitalize == name.capitalize
|
||
::SOAP::Mapping.define_singleton_method(self, qname.name) do |*arg|
|
||
__send__(name, *arg)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
{% endhighlight %}
|
||
</pre>
|
||
|
||
### Le fichier généré default.rb
|
||
|
||
<pre>
|
||
{% highlight ruby %}
|
||
require 'xsd/qname'
|
||
|
||
class Authentification
|
||
@@schema_type = "authentification"
|
||
@@schema_ns = "http://auth.example.com"
|
||
@@schema_qualified = "true"
|
||
@@schema_element = [["login", "SOAP::SOAPString"], ["pwd", "SOAP::SOAPString"]]
|
||
|
||
attr_accessor :login
|
||
attr_accessor :pwd
|
||
|
||
def initialize(login = nil, pwd = nil)
|
||
@login = login
|
||
@pwd = pwd
|
||
end
|
||
end
|
||
|
||
class Response
|
||
@@schema_type = "response"
|
||
@@schema_ns = "http://auth.example.com/"
|
||
@@schema_qualified = "true"
|
||
@@schema_element = [["result", "SOAP::SOAPString"]]
|
||
|
||
attr_accessor :result
|
||
|
||
def initialize(result = nil)
|
||
@result = result
|
||
end
|
||
end
|
||
{% endhighlight %}
|
||
</pre>
|
||
|
||
|
||
Du coup, il ne reste plus qu’a créer un nouvelle objet proxy (qui porte le nom du service et que l’on retrouve dans le fichier `defaultDriver.rb`), puis d’envoyer les messages qui vont bien avec les paramètres.
|
||
|
||
<pre>
|
||
{% highlight ruby %}
|
||
require 'defaultDriver'
|
||
auth_proxy = Authentification.new
|
||
response = auth_proxy.authentification(:login => 'james', :pwd => 'bond')
|
||
puts response.result
|
||
{% endhighlight %}
|
||
</pre>
|
||
|
||
|
||
Au final, ça fonctionne plutôt bien et c’est en peut de temps que l’on a pu mettre en place cet appel. Soap est un protocole de communication verbeux, mais il a l’avantage d’être très bien intégré dans les divers langages et framework, et, dans notre cas, nous avons plusieurs technologie qui utilise ce service. On préfèrerais bien sur voir ici l’utilisation d’un service [rest](http://fr.wikipedia.org/wiki/Representational_State_Transfer) mais il faudrais dans ce cas que les autres technologie soit capable de l’utiliser, et pour certaine techno un peu vieillissante, c’est pas facile (de plus reprendre le code existant a un coût).
|
||
|
||
Je n’aime pas non plus le code généré pour plusieurs raison dont le manque de test. On sent aussi qu’avec les capacités dynamique de [Ruby](http://www.ruby-lang.org/) on pourrais ne pas générer du code, mais construire *à la volé* les appels au service soap.
|
||
|