I am trying to connect to a company that uses SOAP remote procedure calls so I am using the Savon Gem. Here is what I have to setup the authentication:
require 'savon'
client = Savon.client(
wsdl: 'https://customdomain.companydomain.com:port/wsdlcode/wsdl/xmlBridge',
endpoint: 'https://customdomain.companydomain.com:port/endpointcode/soap/xmlBridge',
basic_auth: ["username", "password"],
env_namespace: :soapenv,
namespace_identifier: :web,
logger: Rails.logger,
log_level: :debug,
log: true,
:pretty_print_xml => true,
encoding: 'UTF-8'
)
This seems to work. I can even call "client.operations" to get a list of the functions that can be called. When I run
response = client.call(:get_active_employees)
to try to get an employee list, I get the error message:
Savon::SOAPFault ((SOAP-ENV:Server) The namespace prefix is not allowed to start with the reserved string "xml".)
Does anyone know how to fix this?
The issue here is that the wsdl is likely including incorrect namespaces that Savon is automatically adding into the xml that you are sending back in the request. If you look at the lib/savon/builder.rb file in the Savon Gem by using: gem which savon
to determine where your salon gem is located and then opening the enclosing gem folder in your IDE. Which should be something along the lines of /Users/username/.rvm/gems/ruby-2.5.1/gems/savon-2.13.0/
if your on a mac
Open the builder.rb file lib/savon/builder.rb
Here you'll see a method namespaces which unfortunately includes all the parsed namespaces from the wsdl:
@wsdl.parser.namespaces.each do |identifier, path|
next if namespaces.key?("xmlns:#{identifier}")
namespaces["xmlns:#{identifier}"] = path
end
end
If that wsdl includes an invalid namespace it will be included right back in the output when you perform your call.
To fix this you need to add a new file in config/initializers/savon_monkey_patch.rb
that contains the attached code:
Savon::Builder.class_eval do
private
def namespaces
@namespaces ||= begin
namespaces = Savon::Builder::SCHEMA_TYPES.dup
# check namespace_identifier
namespaces["xmlns#{namespace_identifier.nil? ? '' : ":#{namespace_identifier}"}"] =
@globals[:namespace] || @wsdl.namespace
# check env_namespace
namespaces["xmlns#{env_namespace && env_namespace != "" ? ":#{env_namespace}" : ''}"] =
Savon::Builder::SOAP_NAMESPACE[@globals[:soap_version]]
if @wsdl&.document
@wsdl.parser.namespaces.each do |identifier, path|
# this is to get rid of the xml namespaces that are invalid in a soap document
next if namespaces.key?("xmlns:#{identifier}") || identifier.match?(/^xml/)
namespaces["xmlns:#{identifier}"] = path
end
end
namespaces
end
end
end
The key line being identifier.match?(/^xml/)
which will exclude any namespaces that begin with xml from your outgoing request. I hope this helps.