What's the best way to use SOAP with Ruby?

Ruby on-RailsRubySoap

Ruby on-Rails Problem Overview


A client of mine has asked me to integrate a 3rd party API into their Rails app. The only problem is that the API uses SOAP. Ruby has basically dropped SOAP in favor of REST. They provide a Java adapter that apparently works with the Java-Ruby bridge, but we'd like to keep it all in Ruby, if possible. I looked into soap4r, but it seems to have a slightly bad reputation.

So what's the best way to integrate SOAP calls into a Rails app?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

I built Savon to make interacting with SOAP webservices via Ruby as easy as possible.
I'd recommend you check it out.

Solution 2 - Ruby on-Rails

We used the built in soap/wsdlDriver class, which is actually SOAP4R. It's dog slow, but really simple. The SOAP4R that you get from gems/etc is just an updated version of the same thing.

Example code:

require 'soap/wsdlDriver'

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

That's about it

Solution 3 - Ruby on-Rails

We switched from Handsoap to Savon.

Here is a http://www.dzone.com/links/savon_handsoap_shootout_two_popular_ruby_soap_cli.html">series of blog posts comparing the two client libraries.

Solution 4 - Ruby on-Rails

I also recommend Savon. I spent too many hours trying to deal with Soap4R, without results. Big lack of functionality, no doc.

Savon is the answer for me.

Solution 5 - Ruby on-Rails

Try SOAP4R

And I just heard about this on the Rails Envy Podcast (ep 31):

Solution 6 - Ruby on-Rails

Just got my stuff working within 3 hours using Savon.

The Getting Started documentation on Savon's homepage was really easy to follow - and actually matched what I was seeing (not always the case)

Solution 7 - Ruby on-Rails

Kent Sibilev from Datanoise had also ported the Rails ActionWebService library to Rails 2.1 (and above). This allows you to expose your own Ruby-based SOAP services. He even has a scaffold/test mode which allows you to test your services using a browser.

Solution 8 - Ruby on-Rails

I have used HTTP call like below to call a SOAP method,

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")
*/

Hope it helps. Cheers.

Solution 9 - Ruby on-Rails

I have used SOAP in Ruby when i've had to make a fake SOAP server for my acceptance tests. I don't know if this was the best way to approach the problem, but it worked for me.

I have used Sinatra gem (I wrote about creating mocking endpoints with Sinatra here) for server and also Nokogiri for XML stuff (SOAP is working with XML).

So, for the beginning I have create two files (e.g. config.rb and responses.rb) in which I have put the predefined answers that SOAP server will return. In config.rb I have put the WSDL file, but as a string.

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

In responses.rb I have put samples for responses that SOAP server will return for different scenarios.

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

So now let me show you how I have actually created the server.

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

I hope you'll find this helpful!

Solution 10 - Ruby on-Rails

I was having the same issue, switched to Savon and then just tested it on an open WSDL (I used http://www.webservicex.net/geoipservice.asmx?WSDL) and so far so good!

https://github.com/savonrb/savon

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjcobyView Question on Stackoverflow
Solution 1 - Ruby on-RailsrubiiiView Answer on Stackoverflow
Solution 2 - Ruby on-RailsOrion EdwardsView Answer on Stackoverflow
Solution 3 - Ruby on-RailsphoetView Answer on Stackoverflow
Solution 4 - Ruby on-RailsBruno DuyéView Answer on Stackoverflow
Solution 5 - Ruby on-RailsJason NavarreteView Answer on Stackoverflow
Solution 6 - Ruby on-RailsChrisWView Answer on Stackoverflow
Solution 7 - Ruby on-RailsPhilippe MonnetView Answer on Stackoverflow
Solution 8 - Ruby on-RailsRajaView Answer on Stackoverflow
Solution 9 - Ruby on-RailsRadu RosuView Answer on Stackoverflow
Solution 10 - Ruby on-RailsEcommerce-TechnicianView Answer on Stackoverflow