Quelle est la meilleure façon d'utiliser SOAP avec Ruby?

91

Un de mes clients m'a demandé d'intégrer une API tierce dans leur application Rails. Le seul problème est que l'API utilise SOAP. Ruby a essentiellement abandonné SOAP en faveur de REST. Ils fournissent un adaptateur Java qui fonctionne apparemment avec le pont Java-Ruby, mais nous aimerions tout garder dans Ruby, si possible. J'ai regardé dans soap4r, mais il semble avoir une légère mauvaise réputation.

Alors, quelle est la meilleure façon d'intégrer les appels SOAP dans une application Rails?

jcoby
la source

Réponses:

36

Nous avons utilisé la soap/wsdlDriverclasse intégrée , qui est en fait SOAP4R. C'est un chien lent, mais vraiment simple. Le SOAP4R que vous obtenez de gems / etc n'est qu'une version mise à jour de la même chose.

Exemple de code:

require 'soap/wsdlDriver'

client = SOAP::WSDLDriverFactory.new( 'http://example.com/service.wsdl' ).create_rpc_driver
result = client.doStuff();

C'est à peu près ça

Orion Edwards
la source
37
Une partie de la raison pour laquelle il s'agit de "Dog Slow" est que vous créez le proxy à chaque fois que vous vous connectez au service. Vous pouvez éviter cette douleur en utilisant wsdl2ruby pour créer le proxy de manière permanente, puis en appelant le proxy pré-généré.
Steve Weet
6
Nous pourrions, mais cela signifierait installer wsdl2ruby et ainsi de suite. Parfois, Dog Slow va bien :-)
Orion Edwards
1
Si vous avez besoin de créer des classes proxy pour Savon, vous pouvez suivre l' approche de kredmer consistant à créer des méthodes soap à la volée avec l'aide de SoapUI pour renseigner les noms de méthodes sans avoir à créer un analyseur wsdl personnalisé :). Au lieu de stocker toutes les méthodes en mémoire, vous pouvez écrire dans un fichier, surtout si vous en avez des tonnes.
Dejan
3
04/2015: Soap4r est mort, le site Web est en panne. Il semble que Savon soit le choix commun en ce moment.
Puce
J'ai fouillé dans cet espace et découvert soap4r-ng, qui est toujours maintenu github.com/rubyjedi/soap4r
Ghoti
170

J'ai créé Savon pour rendre l'interaction avec les services Web SOAP via Ruby aussi simple que possible.
Je vous recommande de le vérifier.

rubiii
la source
5
+1 pour le savon, pas pour dénigrer soap4r - mais j'ai eu une très mauvaise expérience avec. Manque de bonne documentation et trop encombrant.
konung
1
Agréable! Le monde SOAP en rubis s'est amélioré depuis la dernière fois que j'ai dû utiliser Soap4R pour le faire (il y a ~ 18 mois)
madlep
l'un de vous peut-il m'aider s'il vous plaît à frapper l'api de sabre en utilisant du savon? J'ai un code qui savon me fournissant les méthodes utilisant wsdl du SOAP mais je ne peux pas envoyer la demande en utilisant savon au format xml.
Jai Kumar Rajput
5

Je recommande également Savon . J'ai passé trop d'heures à essayer de gérer Soap4R, sans résultat. Gros manque de fonctionnalité, pas de doc.

Savon est la réponse pour moi.

Bruno Duyé
la source
3

Je viens de faire fonctionner mes affaires dans les 3 heures avec Savon.

La documentation de mise en route sur la page d'accueil de Savon était vraiment facile à suivre - et correspondait en fait à ce que je voyais (pas toujours le cas)

ChrisW
la source
2

Kent Sibilev de Datanoise avait également porté la bibliothèque Rails ActionWebService vers Rails 2.1 (et supérieur). Cela vous permet d'exposer vos propres services SOAP basés sur Ruby. Il dispose même d'un mode échafaudage / test qui vous permet de tester vos services à l'aide d'un navigateur.

Philippe Monnet
la source
2

J'ai utilisé SOAP dans Ruby lorsque j'ai dû créer un faux serveur SOAP pour mes tests d'acceptation. Je ne sais pas si c'était la meilleure façon d'aborder le problème, mais cela a fonctionné pour moi.

J'ai utilisé Sinatra gem (j'ai écrit sur la création de points de terminaison moqueurs avec Sinatra ici ) pour le serveur et aussi Nokogiri pour les trucs XML (SOAP fonctionne avec XML).

Donc, pour le début j'ai créé deux fichiers (par exemple config.rb et answers.rb) dans lesquels j'ai mis les réponses prédéfinies que le serveur SOAP renverra. Dans config.rb, j'ai mis le fichier WSDL, mais sous forme de chaîne.

@@wsdl = '<wsdl:definitions name="StockQuote"
         targetNamespace="http://example.com/stockquote.wsdl"
         xmlns:tns="http://example.com/stockquote.wsdl"
         xmlns:xsd1="http://example.com/stockquote.xsd"
         xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
         xmlns="http://schemas.xmlsoap.org/wsdl/">
         .......
      </wsdl:definitions>'

Dans answers.rb, j'ai mis des exemples de réponses que le serveur SOAP retournera pour différents scénarios.

@@login_failure = "<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <LoginResponse xmlns="http://tempuri.org/">
            <LoginResult xmlns:a="http://schemas.datacontract.org/2004/07/WEBMethodsObjects" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
                <a:Error>Invalid username and password</a:Error>
                <a:ObjectInformation i:nil="true"/>
                <a:Response>false</a:Response>
            </LoginResult>
        </LoginResponse>
    </s:Body>
</s:Envelope>"

Alors maintenant, laissez-moi vous montrer comment j'ai créé le serveur.

require 'sinatra'
require 'json'
require 'nokogiri'
require_relative 'config/config.rb'
require_relative 'config/responses.rb'

after do
# cors
headers({
    "Access-Control-Allow-Origin" => "*",
    "Access-Control-Allow-Methods" => "POST",
    "Access-Control-Allow-Headers" => "content-type",
})

# json
content_type :json
end

#when accessing the /HaWebMethods route the server will return either the WSDL file, either and XSD (I don't know exactly how to explain this but it is a WSDL dependency)
get "/HAWebMethods/" do
  case request.query_string
    when 'xsd=xsd0'
        status 200
        body = @@xsd0
    when 'wsdl'
        status 200
        body = @@wsdl
  end
end

post '/HAWebMethods/soap' do
request_payload = request.body.read
request_payload = Nokogiri::XML request_payload
request_payload.remove_namespaces!

if request_payload.css('Body').text != ''
    if request_payload.css('Login').text != ''
        if request_payload.css('email').text == some username && request_payload.css('password').text == some password
            status 200
            body = @@login_success
        else
            status 200
            body = @@login_failure
        end
    end
end
end

J'espère que vous trouverez cela utile!

Radu Rosu
la source
0

J'ai utilisé un appel HTTP comme ci-dessous pour appeler une méthode SOAP,

require 'net/http'

class MyHelper
  def initialize(server, port, username, password)
    @server = server
    @port = port
    @username = username
    @password = password

    puts "Initialised My Helper using #{@server}:#{@port} username=#{@username}"
  end



  def post_job(job_name)

    puts "Posting job #{job_name} to update order service"

    job_xml ="<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ns=\"http://test.com/Test/CreateUpdateOrders/1.0\">
    <soapenv:Header/>
    <soapenv:Body>
       <ns:CreateTestUpdateOrdersReq>
          <ContractGroup>ITE2</ContractGroup>
          <ProductID>topo</ProductID>
          <PublicationReference>#{job_name}</PublicationReference>
       </ns:CreateTestUpdateOrdersReq>
    </soapenv:Body>
 </soapenv:Envelope>"

    @http = Net::HTTP.new(@server, @port)
    puts "server: " + @server  + "port  : " + @port
    request = Net::HTTP::Post.new(('/XISOAPAdapter/MessageServlet?/Test/CreateUpdateOrders/1.0'), initheader = {'Content-Type' => 'text/xml'})
    request.basic_auth(@username, @password)
    request.body = job_xml
    response = @http.request(request)

    puts "request was made to server " + @server

    validate_response(response, "post_job_to_pega_updateorder job", '200')

  end



  private 

  def validate_response(response, operation, required_code)
    if response.code != required_code
      raise "#{operation} operation failed. Response was [#{response.inspect} #{response.to_hash.inspect} #{response.body}]"
    end
  end
end

/*
test = MyHelper.new("mysvr.test.test.com","8102","myusername","mypassword")
test.post_job("test_201601281419")
*/

J'espère que ça aide. À votre santé.

Raja
la source