Savon - SOAP - ruby - 400 Bad Request - ruby

I'm trying to use Savon to make a SOAP request with Ruby, but I'm receiving a 400 Bad Request response from the server.
This is the request I'm trying to make (according to soapUI):
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:apis="http://www.csisoftwareusa.com/ApiService">
<soap:Header/>
<soap:Body>
<apis:AuthenticateConsumer>
<!--Optional:-->
<apis:consumerName>?</apis:consumerName>
<!--Optional:-->
<apis:consumerPassword>?</apis:consumerPassword>
</apis:AuthenticateConsumer>
</soap:Body>
</soap:Envelope>
Here is the request that I make with Ruby; it returns a 400 Bad Request error:
<SOAP-ENV:Envelope>
<SOAP-ENV:Body>
<ins0:AuthenticateConsumer>
<ins0:consumerName>?</ins0:consumerName>
<ins0:consumerPassword>?</ins0:consumerPassword>
</ins0:AuthenticateConsumer>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Http Headers: SOAPAction: "http://www.csisoftwareusa.com/ApiService/AuthenticateConsumer", Content-Type: text/xml;charset=UTF-8, Content-Length: 504
Here is the request that I was able to make with Python. THIS request succeeds:
<SOAP-ENV:Envelope>
<SOAP-ENV:Header/>
<ns0:Body>
<ns1:AuthenticateConsumer>
<ns1:consumerName>?</ns1:consumerName>
<ns1:consumerPassword>?</ns1:consumerPassword>
</ns1:AuthenticateConsumer>
</ns0:Body>
</SOAP-ENV:Envelope>
Http headers: {'SOAPAction': u'"http://www.csisoftwareusa.com/ApiService/AuthenticateConsumer"', 'Content-Type': 'text/xml; charset=utf-8'}
I need to integrate calls to this API into a Rails application, so doing it in Python isn't a valid solution.
I'm wondering if anyone can see what I'm missing. Is the empty <SOAP-ENV:Header /> tag the issue, and if so, how can I add that to the Savon request?
Thanks,
Stuart

In my case, I had repeated namespaces, so I was getting 400 Bad Request.
My code:
require 'savon'
namespaces = {
"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",
}
client = Savon.client(
...
:namespace => namespaces
}
I removed the :namespace option and the error was gone.
How did I find the error?
Use build_request instead of call and print the request body:
client.build_request(:search, message: {...})
puts request.body
I took the request body and pasted it into SoapUI, then I made changes one by one until the request was successful.

The issue here is with my http headers: Because the url has spaces in it, the url has to be encoded. However, I have to encode it before passing it to Savon, which then encodes it again - this double-encoded url fails. See: https://stackoverflow.com/questions/14482251/ruby-savon-soap-request-double-escapes-spaces-in-url
Thanks,
Stuart

Related

SOAP: HTTP Error 400. The request has an invalid header name

I am making a SOAP call with using a Ruby On Rails gem Savon and when making a request, I get this error message:
Bad Request - Invalid Header
HTTP Error 400. The request has an invalid header name.
It sounds pretty descriptive, but I am still not able to figure out the problem. I also found a few topics here on SO, but none of them has unfortunately helped me.
Here's how I do make the connection:
client = Savon.client(endpoint: wsdl_url,
namespace: '',
env_namespace: :s,
basic_auth: ["username", "password"],
ssl_verify_mode: :none,
log: true,
logger: Rails.logger,
pretty_print_xml: true)
#response = client.call('MyAction',
soap_action: 'url address',
xml: xml_payload,
headers: {
'Content-Type': 'application/soap+xml; charset=utf-8',
})
And here's the generated error from the (terminal) console:
SOAP response (status 400)
<?xml version="1.0"?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"/>
<BODY><h2>Bad Request - Invalid Header</h2>
<hr><p>HTTP Error 400. The request has an invalid header name.</p>
</hr></BODY>
</HEAD>
</HTML>
If I change the header config from this:
#response = client.call('MyAction',
soap_action: 'url address',
xml: xml_payload,
headers: {
'Content-Type': 'application/soap+xml; charset=utf-8',
})
to this:
#response = client.call('MyAction',
soap_action: 'url address',
xml: xml_payload)
The error message says a lot less:
Savon::HTTPError - HTTP error (400):
So the header makes it a bit more descriptive. If I look at the headers generated by Savon, it looks like this:
SOAP request: https://URL_ENDPOINT
Content-Type: application/soap+xml; charset=utf-8, SOAPAction: "http://URL_ENDPOINT/MyAction", Content-Type: text/xml;charset=UTF-8, Content-Length: 2935
What headers are wrong? How do I debug/inspect it here?
I also tried to investigate this issue in Postman and got only this error message:
400 Bad Request - The request cannot be fulfilled due to bad syntax.
I also tried to validate the generated XML data I send to the server - and that's valid (verified on different validators).
How do I solve this problem?
Thank you

Creating Multiple Transactions based on Request

I'm mocking out a soap webservice and I can only get the default first response to return regardless of the request body.
I'm basing my attempts off the docs Multiple Transaction Examples and I'm confused as to what I'm doing wrong.
As an example:
+ Request
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="" xmlns:xsd="" xmlns:xsi="">
<SOAP-ENV:Body>
<m:transaction-identity-verification xmlns:m="">
</m:transaction-identity-verification>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
+ Response
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="">
<env:Header />
<env:Body>
<java:transaction-response xmlns:java="j">
<transaction-status>
<transaction-id>third_8020750179321</transaction-id>
<transaction-request-id>george_8020860578800</transaction-request-id>
<accounts-transaction-id>13</accounts-transaction-id>
<reference-id>13</reference-id>
<transaction-result>questions</transaction-result>
</transaction-status>
</java:transaction-response>
</env:Body>
</env:Envelope>
+ Request
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="" xmlns:xsd="" xmlns:xsi="">
<SOAP-ENV:Body>
<m:transaction-continue xmlns:m="">
</m:transaction-continue>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
+ Response
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="">
<env:Header/>
<env:Body>
<java:transaction-response xmlns:java="">
<transaction-status>
<transaction-id>cont_1_11020785803682</transaction-id>
<transaction-request-id>11020943348626</transaction-request-id>
<accounts-transaction-id>0</accounts-transaction-id>
<transaction-result>passed</transaction-result>
</transaction-status>
</java:transaction-response>
</env:Body>
</env:Envelope>
In the example above, I will only receive the first response even when I post two different requests. Based on the linked documentation this should be possible.
The Apiary mock-server does not have the capability to determine which response to return from multiple transaction examples based on the body of your request.
API Blueprint does allow you to provide multiple responses, Apiary mock server will only use response-code, headers or content-type to differentiate these examples.
For example, given two responses with different content-types:
+ Response 200 (plain/text)
Text Response
+ Response 200 (application/json)
{ "text": "JSON Response" }
Now, when we make a request to the mock server for the above responses. We can supply an Accept header to get the JSON response:
$ curl -H 'Accept: application/json' URL
{ "text": "JSON Response" }
Or ask for the text response:
$ curl -H 'Accept: plain/text' URL
Text Response
You can find more information regarding this at http://support.apiary.io/knowledgebase/articles/117119-handling-multiple-actions-on-a-single-resource

Ruby sending xml to server

Im new to network programming and now have a problem with sending some xml data to a server.
I have the following code:
require "net/http"
require "net/https"
require "uri"
xml = <<XML
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?><data appname="dhl_entwicklerportal" language-code="de" password="Dhl_123!" request="get-status- for-public-user"><data piece-code="034234"></data></data>
XML
uri = URI('https://cig.dhl.de/services/sandbox/rest/sendungsverfolgung')
nhttp = Net::HTTP.new(uri.host, uri.port)
nhttp.use_ssl=true
nhttp.verify_mode=OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri)
request.basic_auth 'hidden', 'hidden'
response = nhttp.start {|http|
http.request(request, xml: xml)
}
puts response.body
Although i get some response, it is not the right one. For some reason it isn`t sending the body(my xml) properly. If i type the URL manually:
https://cig.dhl.de/services/sandbox/rest/sendungsverfolgung?xml=<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?><data appname="dhl_entwicklerportal" language-code="de" password="Dhl_123!" request="get-status- for-public-user"><data piece-code="034234"></data></data>
it works.
---------------------------EDIT-------------------------------
I edited my code above following the solution from Alex Wayne. Now I get this error:
d:/Ruby200/lib/ruby/2.0.0/net/http/generic_request.rb:179:in `send_request_with_body': undefined method `bytesize' for #<Hash:0x29bcff8> (NoMethodError)
HTTP GET requests cannot have a body, so it the second argument is for data that gets appended to the url as the query string. You probably want to use POST which supports a real request body.
http.request_post(request, xml)
UPDATE: I think I misunderstood...
According to the query string that works, it's working with GET. But it's expecting the XML to be after ?xml=. So you need to encode you request body/query string as a hash with xml as the key and the xml string as the value.
So not this:
http.request(request, xml)
# GET http://domain.com/path?<myxml></myxml>
But this:
http.request(request, xml: xml)
# GET http://domain.com/path?xml=<myxml></myxml>

Bing Translation Api access via SOAP interface from Ruby

I'm trying to use Bing Translator SOAP API (due to in HTTP API I'm getting 414 "Request too long" for not so big requests due to UTF-8 serialization).
So, I'm playing with bing_translator gem source trying to switch it from HTTP inerface to SOAP one using Savon SOAP toolkit.
My workflow as follows (access token getting function not shown):
WSDL_URI = 'http://api.microsofttranslator.com/V2/soap.svc?wsdl'
get_access_token
client = Savon.client(wsdl: WSDL_URI, headers: {'Authorization' => "Bearer #{#access_token['access_token']}"})
params = {
'from' => 'ru',
'to' => 'en',
'text' => 'Это текст для перевода',
'category' => 'general',
'contentType' => 'text/plain'
}
result = client.call(:translate, message: params)
Then SOAP request executes:
SOAP request: http://api.microsofttranslator.com/V2/soap.svc
Authorization: Bearer http%3a%2f%2fschemas.xmlsoap.org%2fws%2f2005%2f05%2fidentity%2fclaims%2fnameidentifier=invest_amurobl_ru&http%3a%2f%2fschemas.microsoft.com%2faccesscontrolservice%2f2010%2f07%2fclaims%2fidentityprovider=https%3a%2f%2fdatamarket.accesscontrol.windows.net%2f&Audience=http%3a%2f%2fapi.microsofttranslator.com&ExpiresOn=1381128612&Issuer=https%3a%2f%2fdatamarket.accesscontrol.windows.net%2f&HMACSHA256=Mw41PMMgw2n6ZVaGRXtwfR0vwMJUyIMltIyd9pa9MqA%3d
SOAPAction: "http://api.microsofttranslator.com/V2/LanguageService/Translate"
Content-Type: text/xml;charset=UTF-8
Content-Length: 454
<?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://tempuri.org/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<wsdl:Translate>
<to>en</to>
<text>Это текст для перевода</text>
<category>general</category>
<contentType>text/html</contentType>
<from>ru</from>
</wsdl:Translate>
</env:Body>
</env:Envelope>
And I'm getting error 500: Unhandled Service Exception
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-US">Unhandled Service Exception</faultstring>
<detail>
<int xmlns="http://schemas.microsoft.com/2003/10/Serialization/">1</int>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
What's may be wrong? Can anyone who already using Bing Translator SOAP API to diff my soap-messages with yourself? Any advices to how to troubleshoot this.
Thanks for attention.
EDIT:
I've checked API with SoapUI, as #SteffenRoller advices and it works. Here is XML generated by SoapUI (values are inserted by hand):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v2="http://api.microsofttranslator.com/V2">
<soapenv:Header/>
<soapenv:Body>
<v2:Translate>
<!--Optional:-->
<v2:text>Текст, который я хочу перевести</v2:text>
<!--Optional:-->
<v2:from>ru</v2:from>
<!--Optional:-->
<v2:to>en</v2:to>
<!--Optional:-->
<v2:contentType>text/plain</v2:contentType>
<!--Optional:-->
<v2:category>general</v2:category>
</v2:Translate>
</soapenv:Body>
</soapenv:Envelope>
As you can see the only difference is that all the tags inside a <Body> are in the v2 namespace. In the XML, generated by Savon this namespace isn't present at all.
So, now question is: How to instruct Savon to use correct namespace for tags inside the message body?
Although, I think, this is a Savon bug, I'll file it to developers, as SoapUI have generated correct XML by the same WSDL, and Savon doesn't.
Okay. This is a known bug: Savon issue #340. It's related to composite WSDL files and won't bi fixed in current major release :-(
So, in our case we need to tell Savon, what is our namespace is, what is it's name (just to align it with WSDL) and prepend each tag's name with this namespace.
The correct code looks like this:
require 'savon'
access_token = "http%3a%2f%2fschemas.xmlsoap.org%2fws...CA9TEs%3d"
client = Savon.client(
wsdl: 'http://api.microsofttranslator.com/V2/soap.svc?wsdl',
namespace: 'http://api.microsofttranslator.com/V2',
namespace_identifier: :v2,
headers: {'Authorization' => "Bearer #{access_token}"},
)
params = {
'v2:text' => 'Это текст для перевода',
'v2:from' => 'ru',
'v2:to' => 'en',
'v2:contentType' => 'text/plain',
'v2:category' => 'general',
}
result = client.call(:translate, message: params)
puts result.body[:translate_response][:translate_result]
Any advices and corrections are welcome. Thanks.

Ruby: Savon SOAP Requests receives 400 and 415 errors

I am trying to make a SOAP request using the ruby library Savon.
I am using the following code:
require "savon"
Savon.configure do |config|
config.soap_version = 2 # use SOAP 1.2
config.raise_errors = false
end
wsdl_logon = Savon::Client.new do
wsdl.document = "https://api.affili.net/V2.0/Logon.svc?wsdl"
end
username = 'XXX'
password = 'YYY'
wsdl_logon.http.headers["Content-Type"] = "text/xml; charset=utf-8"
response = wsdl_logon.request "Logon" do
soap.body = {'Username' => username, 'Password' => password, 'WebServiceType' => 'Product'}
end
if response.http_error?
puts "Http Error!"
puts y response.http_error
else
puts "No Http Error!"
end
But I keep receiving 400 error messages ("bad request"). Or, if I remove the following line
wsdl_logon.http.headers["Content-Type"] = "text/xml; charset=utf-8"
I am receiving 415 error messages ("unsupported media type").
I have been using PHP to make these requests until now, and the following code always worked without problems:
$soap_logon = new SoapClient('https://api.affili.net/V2.0/Logon.svc?wsdl');
$token = $soap_logon->Logon(array(
'Username' => 'XXX',
'Password' => 'YYY',
'WebServiceType' => 'Product'
));
Can anybody point me to the right direction what a possible error source might be? I am completely lost right now.
Thank you for your help.
I did as Tom De Leu suggested, and tried to remove as many differences in the generated SOAP requests in question as possible. But I still keep receiving 400 errors. Any hint on possible reasons for this would be highly appreciated.
This is the (working) Request generated by PHP (linebreaks in XML added for clarity):
POST /V2.0/Logon.svc HTTP/1.1
Host: api.affili.net
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.2.0-8+etch16
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://affilinet.framework.webservices/Svc/ServiceContract1/Logon"
Content-Length: 456
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://affilinet.framework.webservices/types"
xmlns:ns2="http://affilinet.framework.webservices/Svc"
>
<SOAP-ENV:Body>
<ns2:LogonRequestMsg>
<ns1:Username>xxx</ns1:Username>
<ns1:Password>yyy</ns1:Password>
<ns1:WebServiceType>Product</ns1:WebServiceType>
</ns2:LogonRequestMsg>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This is the (not working) request generated by Ruby (again, xml linebreaks added for clarity)
SOAP request: https://api.affili.net/V2.0/Logon.svc
Content-Type: text/xml; charset=utf-8, SOAPAction: http://affilinet.framework.webservices/Svc/ServiceContract1/Logon, Content-Length: 605
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://affilinet.framework.webservices/Svc"
SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://affilinet.framework.webservices/types"
xmlns:ns2="http://affilinet.framework.webservices/Svc"
>
<SOAP-ENV:Body>
<ns2:LogonRequestMsg>
<ns1:Username>XXX</ns1:Username>
<ns1:Password>YYY</ns1:Password>
<wsdl:WebServiceType>Product</wsdl:WebServiceType>
</ns2:LogonRequestMsg>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
HTTPI executes HTTP POST using the httpclient adapter
SOAP response (status 400):
I found that I needed to add the headers in to get past the 415 error.
Savon.client(wsdl: "www.sample_doman.com/endpoint?wsdl", headers: {'Content-Type' => 'application/soap+xml; charset=utf-8'})
I would suggest looking at the XML sent by the PHP code, then comparing it with the XML sent by the Ruby Savon code, and check where the differences are. Then see whether you can modify your ruby code to generate the correct request.
Telling Savon to use SOAP version 2 (really 1.2) and then manually setting the content type to text/xml kind of defeats the purpose.
If your web service requires SOAP 1.2, then it is expecting a content type of 'application/soap+xml', which SAVON will do for you if you set the soap_version to 2.
If you want a content type of text/xml, just set your soap_version config variable to 1

Resources