I've this code that uses Savon that I'm trying get to work.
require "savon"
require "time"
Gyoku.configure do |config|
config.convert_symbols_to(&:tap)
end
client = Savon::Client.new("https://www.cfhdocmail.com/TestAPI2/DMWS.asmx?WSDL")
response = client.request(:CreateMailing) do
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"
}
end
The output looks like this
<?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="https://www.cfhdocmail.com/LiveAutoAPI/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="https://www.cfhdocmail.com/LiveAutoAPI/">
<env:Body>
<ins0:CreateMailing>
<ins0:Username>Username</ins0:Username>
<ins0:Password>Password</ins0:Password>
<ins0:ProductType>A4Letter</ins0:ProductType>
<ins0:IsMono xsi:nil="true" />
<ins0:IsDuplex>true</ins0:IsDuplex>
<ins0:DespatchASAP xsi:nil="true" />
<ins0:DespatchDate>2012-04-09 00:00:00 +0200</ins0:DespatchDate>
<ins0:AddressNameFormat>Full Name</ins0:AddressNameFormat>
<ins0:ReturnFormat>JSON</ins0:ReturnFormat>
</ins0:CreateMailing>
</env:Body>
</env:Envelope>
But I want it to look like this.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CreateMailing xmlns="https://www.cfhdocmail.com/LiveAutoAPI/">
<Username>Username</Username>
<Password>Password</Password>
<ProductType>A4Letter</ProductType>
<IsMono/>true</IsMono>
<IsDuplex>true</IsDuplex>
<DespatchASAP>false</DespatchASAP>
<DespatchDate>2012-04-09 00:00:00 +0200</DespatchDate>
<AddressNameFormat>Full Name</AddressNameFormat>
<ReturnFormat>JSON</ReturnFormat>
</CreateMailing>
</soap:Body>
</soap:Envelope>
How do I do that?
I'm getting this error at the moment
Server did not recognize the value of HTTP Header SOAPAction: CreateMailing. (Savon::SOAP::Fault)
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.
There might be a better way to do it, but the only way I could figure out how to get the XML body exactly how I needed it was to build my own XML. I have a to_xml method in my model and it gets called like soap.body = { to_xml }.
def to_xml
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
xml.Username "Username"
xml.Password "Password"
end
Related
In Savon 1, I could use the soap.input to add xmlns like following:
soap_client = Savon.client("http://pathtowsdl.com/a.svc?wsdl")
response = soap_client.request "AnAction" do
http.headers["soapAction"] = "AnAction"
soap.input = ["AnAction", {"xmlns" => "http://apathtosomething.com"}]
soap.body = {
"SomeAttribute" => "SomeValue"
}
end
In Savon 2, I can do client.call(:authenticate, message_tag: :authenticationRequest)
but how to add the xmlns to the authenticationRequest tag?
You have to add the attribute to the call, eg.
client.call('CreateRequest', :attributes => { 'xmlns' => 'xyz' })
#!/usr/bin/env ruby
require 'savon'
soap_client = Savon.client( endpoint: 'http://example.com',
namespace: 'http://v1.example.com')
soap_client.call(:authenticate,
message_tag: :authenticationRequest,
:attributes => { "xmlns" => "http://apathtosomething.com" })
outputs
<?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://v1.example.com"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<wsdl:authenticationRequest xmlns="http://apathtosomething.com">
</wsdl:authenticationRequest>
</env:Body>
</env:Envelope>
How can I add an encoding attribute to the body tag using Savon?
Some background:
I am trying to use savon to connect to a SOAP resource. I can get the WSDL file and browse through the methods.
#client = Savon::Client.new("http://some.domain.com/v2messaging/service?WSDL")
when I try to use the login method
response = #client.request :service, :login do
soap.body = {
"String_1" => "username",
"String_2" => "password"
}
end
I get this error:
Failure/Error: response = #client.request :service, :login do Savon::SOAP::Fault: (env:Client) caught exception while handling request: unexpected encoding style: expected=http://schemas.xmlsoap.org/soap/encoding/, actual
The difference in the body tag. Here is the expected xml (found through SOAPUI application):
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:service="etapestryAPI/service">
<env:header/>
<env:body>
<service:login env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<String_1>username</String_1>
<String_2>password</String_2>
</service:login>
</env:body>
</env:Envelope>
Savon sends:
<?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:service="etapestryAPI/service" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="http://java.sun.com/jax-rpc-ri/internal" xmlns:ins1="etapestryAPI/service">
<env:Body>
<service:login>
<String_1>username</String_1>
<String_2>password</String_2>
</service:login>
</env:Body>
</env:Envelope>
There are a few difference between these, but the error returned has to do with the env:encodingStyle attribute on the env:login tag. How can add this attribute?
I figured this one out. To add an attribute to the function tag (in this case login), you can pass in an additional parameter to the method:
response = #client.request :service, :login, "env:encodingStyle" => "http://schemas.xmlsoap.org/soap/encoding/" do
soap.body = {
"String_1" => "username",
"String_2" => "password"
}
end
This will probably now work without passing the block.
I have this (working) example in php:
$usernameToken = new SoapHeaderUsernameToken( $password, $username );
$soapHeaders[] = new SoapHeader("urn:localhost-catalog", 'UsernameToken', $usernameToken);
$client = new SoapClient("my_client_url");
$client->__setSoapHeaders( $soapHeaders );
But I want a client in Ruby. I'm using the savon gem, which can do authentication for you. So far, I have the following method:
def my_soap_client
client = Savon::Client.new do
wsdl.document = "my_client_url"
end
client.wsse.credentials "usr", "pass" # Adding :digest doesn't seem to help
return client
end
# I make requests like this:
my_soap_client.request :wsdl, "getProductGroups", :parent_id => 1
I can successfully list available actions, but if I try to make a request, the result is always "(SOAP-ENV:Client) Missing parameter". Also, it doesn't matter if I pass correct credentials or fake ones. That is why I believe the authentication fails.
Hope someone can help!
Using Wireshark sniffer I intercepted SOAP request that your PHP code sends. It looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:examples:helloservice"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns2="urn:localhost-catalog"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header>
<ns2:UsernameToken>
<Password>pass</Password>
<Username>login</Username>
</ns2:UsernameToken>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:getProductGroups>
<parentId>1</parentId>
</ns1:getProductGroups>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Here you can see that this code adds ns2 namespace for UsernameToken header.
Then I intercepted SOAP request that sends your Ruby code:
<?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://www.ecerami.com/wsdl/HelloService.wsdl"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-1"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>usr</wsse:Username>
<wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
pass
</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</env:Header>
<env:Body>
<wsdl:getProductGroups>
<parentId>1</parentId>
</wsdl:getProductGroups>
</env:Body>
</env:Envelope>
You can see the difference - UsernameToken is wrapped into Security element and namespace for UsernameToken is "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
Your PHP code uses custom authorization and savon does not support it via wsse property. I looked into Savon source code, it uses Akami.wsse and in its code there is no possibility to set namespace. This means you need to manually construct headers. Here is the code in Ruby that generates SOAP request like you possibly need:
require 'savon'
client = Savon::Client.new do
wsdl.document = "test.wsdl"
end
client.request :wsdl, "sayHello" do
soap.namespaces["xmlns:ns2"] = "urn:localhost-catalog"
soap.header = { "ns2:UsernameToken" =>
{"Password" => "pass", "Username" => "login"}
}
soap.body = { :parent_id => 1 }
end
It generate the following SOAP request:
<?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://www.ecerami.com/wsdl/HelloService.wsdl"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns2="urn:localhost-catalog">
<env:Header>
<ns2:UsernameToken>
<Password>pass</Password>
<Username>login</Username>
</ns2:UsernameToken>
</env:Header>
<env:Body>
<wsdl:sayHello>
<parentId>1</parentId>
</wsdl:sayHello>
</env:Body>
</env:Envelope>
Hope this helps!
to set a header in Savon use the following expression:
client.request :wsdl, "getProductGroups" do
soap.header = { :header_key => "your header goes here",
:attributes! => { :attr => "your attribute" }
}
soap.body = { :parent_id => 1 }
end
perhaps that helps you.
-st
I admit I'm new to both SOAP and Savon, but I got it working with another service, but this one is stumping me.
The WSDL is here:
http://stg-wholesale.carsdirect.com/ws/services/Ping?WSDL
The Document when done should look like this:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<dealerPing xmlns="http://wholesale.carsdirect.com">
<request>
<partner>
<pricePlan>integer</pricePlan>
<id>integer</id>
</partner>
<vehicle>
<year>integer</year>
<make>string</make>
<model>string</model>
<trim>string</trim>
</vehicle>
<zipcode>integer</zipcode>
</request>
</dealerPing>
</soap:Body>
</soap:Envelope>
If anyone can get close to generating a document like this or give me some pointers I would greatly appreciate it.
I tried doing something like :
client = Savon::Client.new{|wsdl| wsdl.document = #cp.wsdl}
#response = client.dealer_ping do |soap|
...
end
But, I got this error : undefined method `dealer_ping' for #
I'm pretty much stuck at this point. My knowledge of both parts of this equation is lacking.
Thanks.
I don't have access to the service, so I can't try if it works ... but this should probably work:
client = Savon::Client.new do
wsdl.endpoint = "http://stg-wholesale.carsdirect.com/ws/services/Ping"
wsdl.namespace = "http://wholesale.carsdirect.com"
end
client.request(:dealer_ping) do
soap.body = {
:request => {
:partner => {
:price_plan => 123,
:id => 1222
},
:vehicle => {
:year => 2010,
:make => "Aston Martin",
:model => "DBS",
:trim => ""
},
:zipcode => 90245
}
}
end
I'm trying to communicate with a soap service and I know that I should send a SOAP Envelope like this:
POST /webpay_test/SveaWebPay.asmx HTTP/1.1
Host: webservices.sveaekonomi.se
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "https://webservices.sveaekonomi.se/webpay/CreateOrder"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CreateOrder xmlns="https://webservices.sveaekonomi.se/webpay">
<request>
<Order>
<ClientOrderNr>string</ClientOrderNr>
<CustomerReference>string</CustomerReference>
<OrderDate>dateTime</OrderDate>
<CountryCode>string</CountryCode>
<SecurityNumber>string</SecurityNumber>
<CustomerEmail>string</CustomerEmail>
<IsCompany>boolean</IsCompany>
<PreApprovedCustomerId>long</PreApprovedCustomerId>
<AddressSelector>string</AddressSelector>
</Order>
<InvoiceRows>
<ClientInvoiceRowInfo>
<ArticleNr>string</ArticleNr>
<Description>string</Description>
<PricePerUnit>double</PricePerUnit>
<NrOfUnits>double</NrOfUnits>
<Unit>string</Unit>
<VatPercent>int</VatPercent>
<DiscountPercent>int</DiscountPercent>
<ClientOrderRowNr>int</ClientOrderRowNr>
</ClientInvoiceRowInfo>
<ClientInvoiceRowInfo>
<ArticleNr>string</ArticleNr>
<Description>string</Description>
<PricePerUnit>double</PricePerUnit>
<NrOfUnits>double</NrOfUnits>
<Unit>string</Unit>
<VatPercent>int</VatPercent>
<DiscountPercent>int</DiscountPercent>
<ClientOrderRowNr>int</ClientOrderRowNr>
</ClientInvoiceRowInfo>
</InvoiceRows>
</request>
</CreateOrder>
</soap:Body>
</soap:Envelope>
here is the code I've wrote:
client = Savon::Client.new("https://webservices.sveaekonomi.se/webpay_test/SveaWebPay.asmx?wsdl")
res = client.create_order do |soap|
soap.namespace = "https://webservices.sveaekonomi.se/webpay_test/CreateOrder.asmx"
soap.body = { :auth => { :username => "username", :password => "pass", :client_number => "1111" },
:order => { :client_order_nr => "1000000", :customer_reference => "4212", :order_date => Date.today,
:country_code => "SE", :security_number => "1111111111", :is_company => false,
:customer_email => "me#gmail.com", :pre_approved_customer_id => 0 },
:invoice_rows => { :client_invoice_row_info => { :article_nr => "x100", :description => "something cool -- description",
:price_per_unit => 100, :nr_of_units => 3, :unit => "SEK", :vat_percent => 25,
:discount_percent => 0, :client_order_row_nr => "1"},
:client_invoice_row_info => { :article_nr => "x200", :description => "something cooler -- description",
:price_per_unit => 200, :nr_of_units => 2, :unit => "SEK", :vat_percent => 25,
:discount_percent => 0, :client_order_row_nr => "1" }
}
}
end
and it generates this, which is different from what I have as the template and that's why I'm getting an error:
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:wsdl="https://webservices.sveaekonomi.se/webpay_test/CreateOrder.asmx" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<wsdl:CreateOrder>
<invoiceRows>
<clientInvoiceRowInfo>
<clientOrderRowNr>1</clientOrderRowNr>
<pricePerUnit>200</pricePerUnit>
<nrOfUnits>2</nrOfUnits>
<unit>SEK</unit>
<vatPercent>25</vatPercent>
<articleNr>x200</articleNr>
<discountPercent>0</discountPercent>
<description>something cooler -- description</description>
</clientInvoiceRowInfo>
</invoiceRows>
<order>
<customerEmail>me#gmail.com</customerEmail>
<preApprovedCustomerId>0</preApprovedCustomerId>
<countryCode>SE</countryCode>
<clientOrderNr>1000000</clientOrderNr>
<securityNumber>11111111</securityNumber>
<customerReference>4212</customerReference>
<isCompany>false</isCompany>
<orderDate>2010-06-28</orderDate>
</order>
<auth>
<password>pass</password>
<clientNumber>1111</clientNumber>
<username>username</username>
</auth>
</wsdl:CreateOrder>
</env:Body>
</env:Envelope>
and here is the response:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<CreateOrderResponse xmlns="https://webservices.sveaekonomi.se/webpay">
<CreateOrderResult>
<Accepted>false</Accepted>
<ErrorMessage>Failed to create or authorize order</ErrorMessage>
<SveaOrderNr>0</SveaOrderNr>
<RejectionCode>Error</RejectionCode>
<WillBuyInvoices xsi:nil="true" />
<AuthorizeId>0</AuthorizeId>
<AuthorizedAmount xsi:nil="true" />
<ExpirationDate xsi:nil="true" />
</CreateOrderResult>
</CreateOrderResponse>
</soap:Body>
</soap:Envelope>
could anyone tell me how can I solve this problem. and since I'm a newbie when it comes to SOAP would you also tell me if the order of the xml tags in the soap:Body tag is important or not?
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!
You are missing the <request> element.
Try replacing your soap.body with a single hash with a key of ::request and a value of the existing hash record that you have already.
EDIT 1:
Your namespace line within your code should be "https://webservices.sveaekonomi.se/webpay" not the full URL you have there currently.