I need to hit a SOAP service, however, the server won't accept my request but throws an error due to the below header and footer in the request.
As per the logs, the request/response looks like below:
SOAP **request**: http://www.url.co.au/wserv/wslms.php
SOAPAction: "insertlmsdata", Content-Type: text/xml;charset=UTF-8, Content-Length: 938
<?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:tns="urn:lmsdataautomation" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<tns:insertlmsdata>
<lmsinfo>
<userCred>
<clientCode>code</clientCode>
<username>name</username>
<password>ee##!</password>
</userCred>
<lead>
<requestno>3</requestno>
<firstname>Meckinsey</firstname>
<lastname>Meth</lastname>
<email>meckinsey.meth#er.com</email>
<mobileno>4233333333</mobileno>
<keyword>Romali</keyword>
<requestMessage>looking for Romali</requestMessage>
<responseMessage>Romadd</responseMessage>
<operator>you</operator>
<circle>Romaon</circle>
<requestDate>20-04-2017</requestDate>
<shortCode>2222</shortCode>
<category>DOM</category>
<city>Sydney</city>
<destination>Perth</destination>
</lead>
</lmsinfo>
</tns:insertlmsdata>
</env:Body>
</env:Envelope>
HTTPI POST request to www.egggo.co.au (excon)
SOAP **response** (status 200)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:lmsdataautomation" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:insertlmsdataResponse>
<output xsi:type="xsd:string"><output>Either parsed xml is not valid or contains special chars like & "'</output></output>
</ns1:insertlmsdataResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I have looked thoroughly Savon Documentation, couldn't find anything relevant. Can I configure Savon to not add headers, footer, envelope and just the body?
Basically to ignore the below data:
SOAP request: http://www.url.co.au/wserv/wslms.php
SOAPAction: "insertlmsdata", Content-Type: text/xml;charset=UTF-8, Content-Length: 938
Otherwise, the SOAP server rejects the request. Please help, TIA
Adding the body of the request:
client = Savon.client(wsdl: url, log: true, logger: Rails.logger, log_level: :debug, pretty_print_xml: true, encoding: 'UTF-8')
response = client.call(:insertlmsdata, message: {'lmsinfo'=> {'userCred'=> {'clientCode' => 'code', 'username' => 'dom', 'password' => '#pass' }, 'lead' => {'requestno' => 3, 'firstname'=>'Methyl', 'lastname'=> 'Orange', 'email' => 'methyl.orange#egg.com', 'mobileno' => '55555555', 'keyword' => 'atlanta', 'requestMessage' => 'looking for atlant', 'responseMessage' => 'dd', 'operator' => 'you', 'circle' => 'sydney', 'requestDate' => '04-20-2017', 'shortCode' => 2222, 'category'=> 'DOM', 'city' => 'Sydney', 'destination' => 'Atlanta' } } })
Related
In a rails project using Savon.rb I am trying to make a very complex SOAP call. At least complex to the extend that Savon builders are to much trouble so I decided to manipulate the :xml directly.
First of I initiate the client:
#client = Savon.client(
:endpoint => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/BarcodeWebService.svc',
:wsdl => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl')
Then I make the call in the shape of:
#request = #client.build_request(:generate_barcode,
xml: %Q{ ... see soap call (with ruby interpolation) ... }
I add a correctly formatted l Time.now, format: :postnl_api string and the rest is still hard coded. Including the message number.
Following is how the call actually is made by Savon in this case, as retrieved using #request.body.
The SOAP call in my application
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://tempuri.org/"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode
</Action>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken>
<Username>devc_!R4xc8p9</Username>
<Password>xxxxxxxx</Password>
</UsernameToken>
</Security>
</s:Header>
<s:Body>
<wsdl:GenerateBarcode>
<d6p1:Customer>
<d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
<d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
</d6p1:Customer>
<d6p1:Barcode>
<d6p1:Type>3S</d6p1:Type>
<d6p1:Range>DEVC</d6p1:Range>
<d6p1:Serie>1000000-2000000</d6p1:Serie>
</d6p1:Barcode>
</wsdl:GenerateBarcode>
</s:Body>
</s:Envelope>
Then the following is how a call should look as I've seen this one having success in a sandbox environment of the company.
The SOAP call as it should be
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode</Action>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Username>devc_!R4xc8p9</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxxxxxxx</wsse:Password>
</wsse:UsernameToken>
</Security>
</s:Header>
<s:Body>
<GenerateBarcode xmlns:d6p1="http://postnl.nl/cif/domain/BarcodeWebService/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://postnl.nl/cif/services/BarcodeWebService/">
<d6p1:Message>
<d6p1:MessageID>5</d6p1:MessageID>
<d6p1:MessageTimeStamp>28-06-2017 14:15:41</d6p1:MessageTimeStamp>
</d6p1:Message>
<d6p1:Customer>
<d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
<d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
</d6p1:Customer>
<d6p1:Barcode>
<d6p1:Type>3S</d6p1:Type>
<d6p1:Range>DEVC</d6p1:Range>
<d6p1:Serie>1000000-2000000</d6p1:Serie>
</d6p1:Barcode>
</GenerateBarcode>
</s:Body>
</s:Envelope>
The main thing that seems to be off (and this was also the case using the Savon builder btw) is the envelope's attributes and the main operation's :generate_barcode shape as well as its attributes. I don't get why I get the prefix wsdl: before GenerateBarcode.
I tell Savon to take my xml and build it exactly like that, but it does not work. Sending my version as-is returns an Error 400.
EDIT using Chris his part
Using #Chris his answer I was able to make the following call:
Set up call
#client = Savon.client(
:endpoint => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/BarcodeWebService.svc',
:wsdl => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl',
:log => true,
:wsse_auth => [ENV['postnl_username'], ENV['postnl_password']],
:pretty_print_xml => true,
:convert_request_keys_to => :camelcase,
:env_namespace => :s)
message = {
"d6p1:Message" => {
"d6p1:MessageID" => "7",
"d6p1:MessageTimeStamp" => I18n.l( Time.now, format: :postnl_api)
},
"d6p1:Customer" => {
"d6p1:CustomerCode" => "DEVC",
"d6p1:CustomerNumber" => "11223344"},
"d6p1:Barcode" => {
"d6p1:Type" => "3S",
"d6p1:Range" => "DEVC",
"d6p1:Serie" => "1000000-2000000" }
}
#client.call(:generate_barcode, :message => message, :soap_header => { "Action" => "http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode"})
Call
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://tempuri.org/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action>http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode</Action>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-1">
<wsse:Username>devc_!R4xc8p9</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">098fd559930983af31ef6630a0bb0c1974156561</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</s:Header>
<s:Body>
<wsdl:GenerateBarcode>
<d6p1:Message>
<d6p1:MessageID>7</d6p1:MessageID>
<d6p1:MessageTimeStamp>17-07-2017 22:13:35</d6p1:MessageTimeStamp>
</d6p1:Message>
<d6p1:Customer>
<d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
<d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
</d6p1:Customer>
<d6p1:Barcode>
<d6p1:Type>3S</d6p1:Type>
<d6p1:Range>DEVC</d6p1:Range>
<d6p1:Serie>1000000-2000000</d6p1:Serie>
</d6p1:Barcode>
</wsdl:GenerateBarcode>
</s:Body>
</s:Envelope>
Response:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
<faultstring xml:lang="en-US">The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
Ok this works (verified)
#client = Savon.client(
:wsdl => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl',
:log => true,
:wsse_auth => ['devc_!R4xc8p9', 'xxx'],
:pretty_print_xml => true,
:convert_request_keys_to => :camelcase,
:env_namespace => :s,
:namespace_identifier => nil
)
message = {
"d6p1:Message" => {
"d6p1:MessageID" => "10",
"d6p1:MessageTimeStamp" => Time.now.strftime("%d-%m-%Y %H:%M:%S")
},
"d6p1:Customer" => {
"d6p1:CustomerCode" => "DEVC",
"d6p1:CustomerNumber" => "11223344"},
"d6p1:Barcode" => {
"d6p1:Type" => "3S",
"d6p1:Range" => "DEVC",
"d6p1:Serie" => "1000000-2000000" }
}
attributes = { "xmlns:d6p1" => "http://postnl.nl/cif/domain/BarcodeWebService/",
"xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance",
"xmlns" => "http://postnl.nl/cif/services/BarcodeWebService/"}
#client.call(:generate_barcode, :attributes => attributes,
:message => message,
:soap_header => { "Action" => "http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode"})
So the trick was adding :namespace_identifier => nil and sending attributes. Setting namespace_identifier removes the wsdl from GenerateBarcode ans attributes sets some namespaces on GenerateBarcode tag. Now I remember why I hate SOAP so much :(
I am having a hard time figuring out how to match stub_request with XML requests using webmock.
Request:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header>
<RequesterCredentials xmlns="urn:ebay:api:PayPalAPI" xmlns:n1="urn:ebay:apis:eBLBaseComponents" env:mustUnderstand="0">
<n1:Credentials>
<n1:Username>XYZ</n1:Username>
<n1:Password>ABC</n1:Password><n1:Subject/>
<n1:Signature>HUGE-STRING</n1:Signature>
</n1:Credentials>
</RequesterCredentials>
</env:Header>
<env:Body>
<ManageRecurringPaymentsProfileStatusReq xmlns="urn:ebay:api:PayPalAPI">
<ManageRecurringPaymentsProfileStatusRequest xmlns:n2="urn:ebay:apis:eBLBaseComponents">
<n2:Version>124</n2:Version>
<n2:ManageRecurringPaymentsProfileStatusRequestDetails>
<ProfileID>sdaddsadsd</ProfileID>
<n2:Action>Cancel</n2:Action>
</n2:ManageRecurringPaymentsProfileStatusRequestDetails>
</ManageRecurringPaymentsProfileStatusRequest>
</ManageRecurringPaymentsProfileStatusReq>
</env:Body>
</env:Envelope>
Stubs I tried:
stub_request(:any, /.*.sandbox.paypal.com\/2.0\//).
with(:body => WebMock::Matchers::HashIncludingMatcher.new({'n2:Action'=>'Cancel'}),
:headers => {'User-Agent'=>'Ruby'}).
to_return(:status => 200, ...
or
stub_request(:any, /.*.sandbox.paypal.com\/2.0\//).
with(query:
hash_including({ProfileID: 'sdaddsadsd'}),
:headers => {'User-Agent'=>'Ruby'}).
to_return(:status => 200, ...
(etc.)
have failed.
Anyone?
Provided you can save your XML to be stubbed into separate XML file (say request.xml), it is quite easy:
stub_request(:any, 'https://sandbox.paypal.com')
.with(body: File.read('request.xml').strip)
Headers can be omitted.
How to use webmock regex matcher? might help you with URL matching.
P.S. strip is to remove ending line feed
I'm attempting to modify one of the namespaces in my Savon SOAP call. Here is how my request looks:
HTTPI GET request to www.intg.pathway.verosapps.com (excon)
SOAP request: https://www.intg.pathway.verosapps.com/VerosPathway.svc
SOAPAction: "urn:IVerosPathway/VerosPathway_Ping", Content-Type: text/xml;charset=UTF-8, Content-Length: 434
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<VerosPathway_Ping>
<request>
<Credentials>
<UserId>*username*</UserId>
<Password>*password*</Password>
</Credentials>
</request>
</VerosPathway_Ping>
</soapenv:Body>
</soapenv:Envelope>
I've had to do a ton of tweaking since this company expects it's clients to use .NET for their SOAP interaction and we are using Ruby. I am extremely close to getting the format correct, but I need to do one of two things in the Envelope section:
Change "xmlns="http://tempuri.org/" to "xmlns:wsdl="http://tempuri.org/"
OR remove "xmlns="http://tempuri.org/" completely.
Here is my Savon call:
apiClient = Savon.client(endpoint: "https://www.intg.pathway.verosapps.com/VerosPathway.svc", env_namespace: :soapenv, namespace_identifier: nil, logger: Rails.logger, log_level: :debug, log: true, :pretty_print_xml => true, ssl_version: :TLSv1, wsdl: 'https://www.intg.pathway.verosapps.com/VerosPathway.svc?wsdl')
If I add the following line to my Savon call:
namespaces: {"xmlns:wsdl" => "http://tempuri.org/"}
then my request looks like this:
HTTPI GET request to www.intg.pathway.verosapps.com (excon)
SOAP request: https://www.intg.pathway.verosapps.com/VerosPathway.svc
SOAPAction: "urn:IVerosPathway/VerosPathway_Ping", Content-Type: text/xml;charset=UTF-8, Content-Length: 434
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://tempuri.org/">
<soapenv:Body>
<VerosPathway_Ping>
<request>
<Credentials>
<UserId>*username*</UserId>
<Password>*password*</Password>
</Credentials>
</request>
</VerosPathway_Ping>
</soapenv:Body>
</soapenv:Envelope>
So in this case I would just need to remove the "xmlns="http://tempuri.org/" line.
Any suggestions would be greatly appreciated.
Thanks!
you can simply pass the hash with the options as below:
client = Savon.client(
wsdl: "https://example.com?wsdl",
log: true,
log_level: :debug,
pretty_print_xml: true,
namespace_identifier: :mes,
namespaces: {"xmlns:mes" => "http://example.com/messages/","xmlns:glob"=>"http://example.com/globals"}
)
I finally figured it out. All I needed to do was add the following line to my Savon call:
namespace: ""
So my final Savon call looks like:
apiClient = Savon.client(
endpoint: "https://www.intg.pathway.verosapps.com/VerosPathway.svc",
namespace: "",
env_namespace: :soapenv,
namespace_identifier: nil,
logger: Rails.logger,
log_level: :debug,
log: true,
:pretty_print_xml => true,
ssl_version: :TLSv1,
wsdl: 'https://www.intg.pathway.verosapps.com/VerosPathway.svc?wsdl'
)
I am working with the Savon gem (version 2.5.1) for creating a Soap request and need if to be formatted as follows:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:soap1="urn:website.co.uk/soap:callreport7">
<soap:Header>
<soap1:callcreditheaders>
<soap1:company>?</soap1:company>
<soap1:username>?</soap1:username>
<soap1:password>?</soap1:password>
</soap1:callcreditheaders>
</soap:Header>
<soap:Body>
<soap1:Test07a>?</soap1:Test07a>
</soap:Body>
</soap:Envelope>
I have so far come up with the following:
client = Savon.client(
wsdl: "CallReport7.wsdl",
env_namespace: :soap,
soap_header: {
"soap1:callcreditheaders" => {
'soap1:company' => 'XXXX',
'soap1:username' => 'XXXX',
'soap1:password' => 'XXXX'
}
}
)
response = client.call(:test07a, message: "Test" )
This is returning an error when running in the console of:
Savon::SOAPFault: (soap:Server) An internal system error has occurred
If I remove the soap_header hash then the test runs and returns:
<?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>
<Test07aResponse xmlns="website.co.uk/soap:callreport7">
<TestResult>
<datetime>2014-06-06T14:18:35.081125+01:00</datetime>
</TestResult>
</Test07aResponse>
</soap:Body>
</soap:Envelope>
The problem is I need the header information and I cannot work out how to format the request for this.
As usual: First create a valid request using SoapUI. Then build the equivalent (doesn't have to be exact the same) in Savon.
It turned out the problem was with the envelope. I enabled logging in my code so I could see the SOAP produced by Savon and this then allowed me to try the request in SoapUI and do some trial and error testing. Turned out the problem was with the Envelope so the below fixed the problem in my case:
client = Savon.client(
wsdl: "CallReport7.wsdl",
pretty_print_xml: true,
log: true,
env_namespace: :soap,
namespaces: {"xmlns:soap" => "http://www.w3.org/2003/05/soap-envelope", "xmlns:soap1" => "urn:website.co.uk/soap:callreport7"},
soap_header: {
"soap1:callcreditheaders" => {
'soap1:company' => 'XXXX',
'soap1:username' => 'XXXX',
'soap1:password' => 'XXXX'
}
}
)
response = client.call(:test07a, message: "Test" )
The logging line of code was the key to solving this problem for me.
I need to remove the SOAPAction from my request using Savon 2.
This is how I build the client:
client = Savon.client do
wsdl "http://servername:port/PingService?wsdl"
convert_request_keys_to :none
env_namespace :soapenv
namespaces({
'xmlns:pin' => 'http://servername:port/pingService_v1'
})
end
I use the following call to make the request:
client.call(:invoke, message: { "pin:PingReq" => { "pin:PingDB" => "true", "pin:PingG" => "true" }})
This is the request that is sent:
<soapenv:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://support.cxf.transport.mule.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pin="http://servername/pingService_v1">
<soapenv:Body>
<tns:invoke>
<pin:PingReq>
<pin:PingDB>true</pin:PingDB>
<pin:PingG>true</pin:PingG>
</pin:PingReq>
</tns:invoke>
</soapenv:Body>
</soapenv:Envelope>
A valid request would have everything except for the tns:invoke tags.
With Savon 2.x you can roll your own body by doing this:
client.call(:method, xml: "<tns:invoke><pin:PingReq> ...")