consume soap service with ruby and savon - ruby

I'm trying to use ruby and Savon to consume a web service.
The test service is http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2
require 'rubygems'
require 'savon'
client = Savon::Client.new "http://www.webservicex.net/stockquote.asmx?WSDL"
client.get_quote do |soap|
soap.body = {:symbol => "AAPL"}
end
Which returns an SOAP exception. Inspecting the soap envelope, it looks to me that the soap request doesn't have the correct namespace(s).
Can anyone suggest what I can do to make this work?
I have the same problem with other web service endpoints as well.
Thanks,

This is a problem with the way Savon handles Namespaces. See this answer Why is "wsdl" namespace interjected into action name when using savon for ruby soap communication?
You can resolve this by specifically calling soap.input and passing it an array, the first element is the method and the second is a hash containing the namespace(s)
require 'rubygems'
require 'savon'
client = Savon::Client.new "http://www.webservicex.net/stockquote.asmx?WSDL"
client.get_quote do |soap|
soap.input = [
"GetQuote",
{ "xmlns" => "http://www.webserviceX.NET/" }
]
soap.body = {:symbol => "AAPL"}
end

You might find the latest gem uses the method "request" followed by the symbol reference to the method required.
require 'rubygems'
require 'savon'
client = Savon::Client.new "http://www.webservicex.net/stockquote.asmx?WSDL"
client.request :get_quote do |soap|
soap.input = [
"GetQuote",
{ "xmlns" => "http://www.webserviceX.NET/" }
]
soap.body = {:symbol => "AAPL"}
end

Related

Savon: WCF Soap service with wsse auth: AddressFilter mismatch at the EndpointDispatcher

I am trying to create SOAPClient using Savon - rubygem.
Its a WCF soap service with WSSE auth over https. Here is the code that I tried:
require 'savon'
client = Savon::Client.new do
wsdl.document = "https://svc.sxxxxxify.com:8081/ConfSet.svc?wsdl"
config.soap_version = 2
wsse.credentials "aa5#xxasxsaxsh.com", "test123"
end
p client.wsdl.soap_actions
response = client.request :get_user_clients
p response
But I get this error:
http://www.w3.org/2005/08/addressing/soap/fault2012-10-26T06:07:42.247Z2012-10-26T06:12:42.247Zs:Sendera:DestinationUnreachableThe message with To '' cannot be processed at the
receiver, due to an AddressFilter mismatch at the EndpointDispatcher.
Check that the sender and receiver's EndpointAddresses
agree.
.
The message with To '' cannot be processed at the receiver, due to an
AddressFilter mismatch at the EndpointDispatcher. Check that the
sender and receiver's EndpointAddresses agree. (Savon::SOAP::Fault)
Please help me solve this problem
I had the some problem. I've solved the 'To' problem by providing a header entry and a new namespace. The 'Action' header was also necessary though, and I only discovered that after inspecting SoapUI logs. Here is what worked for me:
#service_url = 'https://svc.sxxxxxify.com:8081/ConfSet.svc/service'
#action = 'your_action'
#client = Savon.client(:wsdl => "#{#service_url}?wsdl", :soap_version => 2,
:namespaces => {"xmlns:x" => "http://www.w3.org/2005/08/addressing"},
:soap_header => {"x:To" => #service_url, "x:Action" => "http://tempuri.org/#{#action}"})

Rails savon, two wsdl

I'm developing on Rails and I'm using the gem Savon (http://savonrb.com).
What I have to do is to integrate myself with a service that I wsdl has two, one for authentication and the other for various functions.
There are two wsdl:
"https://.../authentication.asmx?wsdl"
"https://.../lists.asmx?wsdl"
What I do first is this:
client = Savon::Client.new do |wsdl, http|
http.auth.ssl.verify_mode = :none
wsdl.document = "https://.../authentication.asmx?wsdl"
end
response = client.request :soap, :login, :body => {:username => "...", :password => "..."}
Once logged in I would need to use the second wsdl to perform my function, but I can not declare another client within authentication lose the first thing I said.
response = client.request :get_list_collection do
soap.endpoint = URI(URI.escape("https://.../lists.asmx?wsdl"))
end
I've tried many but I found no solution.
Any idea how to solve this problem?

Parse WSDL file with SOAP4R

Is there any example of WSDL Parser using SOAP4R? I'm trying to list all operations of WSDL file but I can't figure it out :( Can you post me some tutorial?
Thx
Maybe that isn't answer you want, but I recommend you switch to Savon. For example, your task looks like this snippet (this example taken from github's savon page):
require "savon"
# create a client for your SOAP service
client = Savon::Client.new("http://service.example.com?wsdl")
client.wsdl.soap_actions
# => [:create_user, :get_user, :get_all_users]
# execute a SOAP request to call the "getUser" action
response = client.request(:get_user) do
soap.body = { :id => 1 }
end
response.body
# => { :get_user_response => { :first_name => "The", :last_name => "Hoff" } }

Case problem in SOAP message tag names using savon

I'm using Ruby 1.9.2 with savon 0.9.2 on Windows 7 Professional 64 bit.
I need to call a web SOAP service that requires a security token that I get from a second web SOAP service. The code I use is as follows:
require 'savon'
client = Savon::Client.new "http://some.url?wsdl"
client.wsdl.soap_actions
start_session_response = client.request :start_session do
soap.input = ["StartSession", {:xmlns => "http://some.schema" } ]
soap.body = { :userName => "User", :password => "password" }
end
do_something_response = client.request :do_something do
soap.input = [ "DoSomething", { :xmlns => "http://some.schema"} ]
soap.body = { :securityToken => start_session_response.to_hash[:start_session_response][:security_token] }
end
This results in XML that looks like:
<?xml version="1.0" encoding="utf-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://some.schema"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<DoSomething xmlns="http://some.schema">
<wsdl:securityToken>
<wsdl:tokenType>sessiontoken</wsdl:tokenType>
<wsdl:token>
.
.
.
</wsdl:token>
</wsdl:securityToken>
</DoSomething>
</env:Body>
</env:Envelope>
Never mind the weird namespace convention (or is that just me) in this XML that is savon doing its thing.
The problem I face is that the tags inside the securitytoken tag all start with a lower case letter where they should be upper case. So <tokenType> and <token> should have been <TokenType> and <Token>.
In my opinion the definition of these tags are all in the WSDL that is used to create the savon client. That definition seems not to be used or used incorrectly.
What can I do to get the correct XML/SOAP message from savon?
For later releases of Savon, you should be able to supply a 'global' option of convert_request_keys_to when you initialize your Savon client:
# In Savon 2
Savon.client wsdl:"http://some.url?wsdl", convert_request_keys_to: :camelcase
According to comments in the source file, it accepts one of :lower_camelcase, :camelcase, :upcase, or :none.
I had a similar problem with Savon and ended up using strings in stead of symbols for my hash keys, you could try something like:
soap.body = { 'TokenType'=> 'some_value', 'Token' => 'some_value' }
Savon uses Gyoku for the conversion of tags I believe. To change the symbol conversion you can insert the following statement:
Gyoku.convert_symbols_to :camelcase # or one of [:none, :lover_camelcase]
hope that helps.

Handle the PUT method in WEBrick

How do I handle PUT requests in WEBrick?
I have tried defining a do_PUT() method in an AbstractServlet class but the method is never invoked.
I had the same problem and got it working by creating my own custom WEBrick::HTTPProxyServer and adding the put method in that.
require "webrick"
require "webrick/httpproxy"
require 'cgi'
class CustomWEBrickProxyServer < WEBrick::HTTPProxyServer
def do_PUT(req, res)
perform_proxy_request(req, res) do |http, path, header|
http.put(path, req.body || "", header)
end
end
# This method is not needed for PUT but I added for completeness
def do_OPTIONS(req, res)
res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT,PUT"
end
end
Then you need to start your proxy server using your own Custom class.
my_proxy_server = CustomWEBrickProxyServer.new :Port=> proxy_port,
:ProxyVia => forward_proxy,
:ProxyURI => forward_proxy,
:RequestCallback => method(:request_callback),
:ProxyContentHandler => method(:response_callback),
:AccessLog => method(:access_log)

Resources