¿Cuál es la mejor forma de usar SOAP con Ruby?

91

Un cliente mío me ha pedido que integre una API de terceros en su aplicación Rails. El único problema es que la API usa SOAP. Ruby básicamente ha abandonado SOAP a favor de REST. Proporcionan un adaptador Java que aparentemente funciona con el puente Java-Ruby, pero nos gustaría mantenerlo todo en Ruby, si es posible. Miré a soap4r, pero parece que tiene una mala reputación.

Entonces, ¿cuál es la mejor manera de integrar las llamadas SOAP en una aplicación Rails?

jcoby
fuente

Respuestas:

36

Usamos la soap/wsdlDriverclase integrada , que en realidad es SOAP4R. Es lento como un perro, pero realmente simple. El SOAP4R que obtienes de gems / etc es solo una versión actualizada de lo mismo.

Código de ejemplo:

require 'soap/wsdlDriver'

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

Eso es todo

Orion Edwards
fuente
37
Parte de la razón por la que esto es "Dog Slow" es que está construyendo el proxy cada vez que se conecta al servicio. Puede evitar este dolor utilizando wsdl2ruby para construir el proxy de forma permanente y luego llamar al proxy pregenerado.
Steve Weet
6
Podríamos, pero eso significaría instalar wsdl2ruby y así sucesivamente. A veces Dog Slow está bien :-)
Orion Edwards
1
Si necesita crear clases de proxy para Savon, puede seguir el enfoque de kredmer de crear métodos de jabón sobre la marcha con la ayuda de SoapUI para completar los nombres de los métodos y no tener que crear un analizador wsdl personalizado :). En lugar de almacenar todos los métodos en la memoria, puede escribir en un archivo, especialmente si tiene toneladas.
Dejan
3
04/2015: Soap4r está muerto, el sitio web no funciona. Parece que Savon es la opción más común en este momento.
Puce
He estado investigando en este espacio y descubrí soap4r-ng, que todavía se mantiene github.com/rubyjedi/soap4r
Ghoti
170

Construí Savon de hacer interactuar con servicios web de SOAP a través de Rubí lo más fácil posible.
Te recomiendo que lo revises.

rubiii
fuente
5
+1 para savon, no para golpear a soap4r, pero tuve una experiencia muy mala con eso. Falta de buena documentación y demasiado engorroso.
Konung
1
¡Agradable! El mundo SOAP en ruby ​​ha mejorado desde la última vez que tuve que usar Soap4R para hacer esto (hace ~ 18 meses)
madlep
¿Alguno de ustedes puede ayudarme a golpear la API de saber usando savon? Tengo un código que savon me proporciona los métodos usando wsdl del SOAP pero no puedo enviar la solicitud usando savon en formato xml.
Jai Kumar Rajput
5

También recomiendo a Savon . Pasé demasiadas horas tratando de lidiar con Soap4R, sin resultados. Gran falta de funcionalidad, no hay documentación.

Savon es la respuesta para mí.

Bruno Duyé
fuente
3

Acabo de hacer que mis cosas funcionen en 3 horas con Savon.

La documentación de Introducción en la página de inicio de Savon fue realmente fácil de seguir, y en realidad coincidía con lo que estaba viendo (no siempre es el caso)

ChrisW
fuente
2

Kent Sibilev de Datanoise también había portado la biblioteca Rails ActionWebService a Rails 2.1 (y superior). Esto le permite exponer sus propios servicios SOAP basados ​​en Ruby. Incluso tiene un modo de prueba / andamio que le permite probar sus servicios usando un navegador.

Philippe Monnet
fuente
2

He usado SOAP en Ruby cuando tuve que hacer un servidor SOAP falso para mis pruebas de aceptación. No sé si esta fue la mejor manera de abordar el problema, pero funcionó para mí.

He usado la gema Sinatra (escribí sobre la creación de puntos finales simulados con Sinatra aquí ) para el servidor y también Nokogiri para XML (SOAP está trabajando con XML).

Entonces, para el principio, he creado dos archivos (por ejemplo, config.rb y answers.rb) en los que he puesto las respuestas predefinidas que devolverá el servidor SOAP. En config.rb he puesto el archivo WSDL, pero como una cadena.

@@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>'

En answers.rb he puesto ejemplos de respuestas que el servidor SOAP devolverá para diferentes escenarios.

@@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>"

Así que ahora déjame mostrarte cómo he creado realmente el servidor.

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

¡Espero que le resulte útil!

Radu Rosu
fuente
0

He usado una llamada HTTP como a continuación para llamar a un método 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")
*/

Espero eso ayude. Salud.

Raja
fuente