Archive

Tag Archives: soap

I’ve been writing a little test SOAP client, which turned out to — as with so many things — not be a straightforward as I expected. SOAP rarely is, though, but unfortunately the service I’m working with doesn’t provide anything as modern as a JSON-emitting REST interface, alas. SOAP it is, then.

First, I quickly discovered that it took some real searching to find anything reasonable in the way of a SOAP client library for Ruby. There’s a bunch of SOAP crap in the Ruby docs, of course: there’s the SOAP module, there’s SOAP::RPC, and tons more. With no documentation whatsoever. I could almost get something to work with that, except that I needed to add an additional header to the SOAP envelope, and…well, there doesn’t seem to be info anywhere on how to do that. Hopeless.

Thankfully, I finally discovered the Savon gem. It’s pretty well-documented and very simple to use. With that, I was quickly able to put together a request…which didn’t work. Hmm. There was some yucky PHP example code for the service I was trying to access, so I ran that with the cool HTTPScoop program going, and looked at the request that it sent, which did work. Some differences there, indeed, and it wasn’t obvious how to fix it. The problem was that the input wasn’t properly specified, and the parameter didn’t have a namespace declared on it. Instead of:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/webservice/schema/">
  <SOAP-ENV:Header>
    <ns1:API-KEY>foobar</ns1:API-KEY>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <ns1:DoSimpleRequest>
      <ns1:uniqueId>12345</ns1:uniqueId>
    </ns1:DoSimpleRequest>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

I was getting this:

<env:Envelope xmlns:wsdl="http://example.com/webservice/schema/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Header>
    <API-KEY>foobar</API-KEY>
  </env:Header>
  <env:Body>
    <wsdl:DoSimple>
      <uniqueId>12345</uniqueId>
    </wsdl:DoSimple>
  </env:Body>
</env:Envelope>

You can see that the input ended up as “DoSimple” instead of “DoSimpleRequest”. My Ruby code was doing response = client.do_simple which of course generated that. But if I changed it to response = client.do_simple_request I got a method missing error. Something about the WSDL wasn’t matching what I needed to send, which was annoying. In addition, the uniqueId parameter didn’t have the namespace prepended to it.

So, what I ended up needing to do with Savon was to specifically provide the input and action, rather than letting it figure them out from the WSDL; and then I needed to force in the namespace declaration for the parameter. The resulting, working code:

require 'rubygems'
require 'savon'

client = Savon::Client.new "http://example.com/the-soap-API.wsdl"
response = client.do_simple do |soap|
  soap.header["API-KEY"] = "foobar"
  soap.input = "DoSimpleRequest"
  soap.action = "DoSimpleRequest"
  body = Hash.new
  body["wsdl:uniqueId"] = 12345
  soap.body = body
end

puts "Response:\n"
puts response

It’s not ideal — normally I’d specify the uniqueId parameter with a nice

  soap.body = {
      :uniqueId => 12345
  }

But that resulted in uniqueId rather than wsdl:uniqueId, and I couldn’t find another way to force it. I looked through the Savon source code, and body is simply a Hash attribute on which it ends up calling @body.to_soap_xml. So I sort of fooled it by creating my own hash and then setting body to it. Obviously a fragile thing to do, so we’ll see if I missed something, and if not whether it makes sense to submit a patch to enable this in a more robust way. In any case, now I get the request that I needed:

<env:Envelope xmlns:wsdl="http://example.com/webservice/schema/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Header>
    <API-KEY>foobar</API-KEY>
  </env:Header>
  <env:Body>
    <wsdl:DoSimpleRequest>
      <wsdl:uniqueId>12345</wsdl:uniqueId>
    </wsdl:DoSimpleRequest>
  </env:Body>
</env:Envelope>

Hope this saves someone some work, and I’ll be sure to follow up with the eventual outcome of this patchwork solution.

Follow

Get every new post delivered to your Inbox.