Why is wsdl namespace interjected into action name when using savon for ruby soap communication?
Steve, you see that wsdl: in front of ProcessMessage tag? - I thought that was the only thing that was throwing me off but its not ( by the way it's hard set in soap.rb in Savon lib on line 160). That even if I don't spacify it in soap.namespaces - it's hard generated and attached in final xml. Which is not allowed by my service.
While the xml that is generated is a valid xml - it's not complete by the requirments of the service I'm trying to talk to. I.e.: in generated xml,
<?xml version="1.0" encoding="UTF-8"?>
tag is missing, also, I need PayloadManifest in the header,plus I need wsu:created and wsu:expires in my wsse: tag, but they are not implemented, etc., etc. a bunch of other little quirks that are too specific to my case. However soap has a private method = xml_body. Also soap lib in to_xml method is checking whether @xml_body was already set, before generating it's own xml. So I ended up slighly modifying behavior of soap. by making soap.xml_body = publicly accessable. So I was able to do:
response = client.process_message! do |soap|
soap.action = "http://www.starstandards.org/webservices/2005/10/transport/operations/ProcessMessage"
soap.xml_body = "MY XML STRING GENERATED ELSEWHERE GOES HERE"
end
Which finally works!!!!
I'll suggest this to rubii - if this option becomes available that will solve a lot rare cases - where people can generate their custom xml and use the rest of savon lib.
consume soap service with ruby and savon
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
Talking with a SOAP service using Savon gem in Ruby
Thanks to Steve, I found "Why is “wsdl” namespace interjected into action name when using savon for ruby soap communication?" where Nick and Steve were talking about a similar problem.
Like Nick, my problem was in the way Savon is cooking up a SOAP envelope. As recommended by Nick, I ended up monkey patching a couple of methods in the Savon SOAP class. It's in lib/savon/soap.rb
and I'm good to go now.
I'm a novice when it comes to SOAP and it's my first time writing a SOAP client, but honestly it SUCKS! I still remember my first time writing a client for a REST service and gosh it was fun.
REST ROCKS, SOAP SUCKS. that's all!
SAVON setting envelope namespace in the envelope
What I have done in the past was the following (I always preferred to go without WSDL). Perhaps you can use it:
#!/usr/bin/env ruby
require 'savon'
additional_ns = {
'xmlns:api' => "http://api.notes.xyz.com/"
}
client = Savon.client(
:endpoint => "http://www.example.com/endpoint",
:namespace => "xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/",
:namespaces => additional_ns,
:log => true,
:log_level => :debug,
:pretty_print_xml => true,
:ssl_verify_mode => :none
)
client.call(....)
Remove namespace from Savon SOAP request
These changes solved my problem.
client.request(:web, "CreateMailer") do |soap|
soap.input = [
"CreateMailer", {
xmlns: "https://www.cfhdocmail.com/LiveAutoAPI/"
}
]
soap.element_form_default = :unqualified
soap.body = {
Username: "Username",
Password: "Password",
ProductType: "A4Letter",
IsMono: false,
IsDuplex: true,
DespatchASAP: false,
DespatchDate: Time.parse("2012-04-09"),
AddressNameFormat: "Full Name",
ReturnFormat: "JSON"
}
client.http.headers.delete("SOAPAction")
end
Thanks @sluukkonen for the help.
How to access wash out soap actions directly with passing action name in query string?
It is possible. Use send
to call the action that you want. Note that this will only work if the other action explicitly calls render
at the end of the action. Also, do not trust the :action_name param. Consider checking the :action_name against a whitelist of allowed actions before using it and raise an error if it's invalid.
class DummySoapController < ApplicationController
soap_service namespace: 'urn:WashOut'
def action
send(params[:action_name])
end
soap_action "convert_to_string", :args => :integer, :return => :string
def convert_to_string
render :soap => params[:value].to_s
end
soap_action "convert_to_integer", :args => :integer, :return => :float
def convert_to_float
render :soap => params[:value].to_f
end
end
For example, to get a string value you would use the URL
http://example.com/dummy_soap/action?action_name=convert_to_string&value=1
Or to get a float
http://example.com/dummy_soap/action?action_name=convert_to_float&value=1
Related Topics
Attr_Accessor Strongly Typed Ruby on Rails
Same Instance Variable for All Actions of a Controller
Hot Deploy on Heroku with No Downtime
Install Bundler Gem Using Ansible
What Does "Wrong Number of Arguments (1 for 0)" Mean in Ruby
How to Get the Latest Record from Each Group in Activerecord
How to Avoid Putting the Magic Encoding Comment on Top of Every Utf-8 File in Ruby 1.9
Ruby/Rails - .Each Iterator Is Printing Entire Array at the End of the Loop
Error Installing Debugger: Failed to Build Gem Native Extension with Ruby-1.9.3-P362
Param Is Missing or the Value Is Empty: User Rails 4
Aws Elastic Beanstalk - How to Upgrade Existing Environment from Ruby 2.1 to Ruby 2.2
How to Access (Devise) Current_User in a Rspec Feature Test