Savon

Heavy metal SOAP client

GitHub RubyGems

Read SOAP Responses

The response provides a few convenience methods for you to work with the XML in any way you want.

Translates the response and returns the SOAP header as a Hash.

response.header  # => { token: "secret" }

#body

Translates the response and returns the SOAP body as a Hash.

response.body  # => { response: { success: true, name: "luke" } }

#to_hash is an alias for #body.

response.to_hash  # => { response: { success: true, name: "luke" } }

#find

Walks the parsed envelope by tag name, returning the value at the given path or nil when a key is missing. Useful when you want to drill into a deeply nested response without chaining [] through Hashes that might be nil.

response.find("Body", "AuthenticateResponse", "Return")
# => { success: true, name: "luke" }

#header and #body are thin wrappers around #find("Header") and #find("Body").

#to_array

Looks up a path under the SOAP body and always returns an Array. Returns [] if any key in the path is nil, wraps a single Hash in an Array, and returns the existing Array compacted otherwise. Handy when a service sometimes returns one element and sometimes many for the same field.

response.to_array(:authenticate_response, :users, :user)
# => [{ id: 1, name: "luke" }, { id: 2, name: "lea" }]

response.to_array(:authenticate_response, :missing)
# => []

#full_hash

Translates the response and returns the full envelope as a Hash, including both header and body.

response.full_hash  # => { envelope: { header: { ... }, body: { ... } } }

Note: response.hash still works but is deprecated and will be removed in a future release.

Savon uses Nori to translate the SOAP response XML to a Hash. You can change how the response is translated through a couple of global and local options. The following example shows the options available to configure Nori and their defaults.

client = Savon.client do
  # Savon defaults to strip namespaces from the response
  strip_namespaces true

  # Savon defaults to convert response XML tags to snakecase Symbols
  convert_response_tags_to ->(tag) { tag.snakecase.to_sym }

  # Savon defaults to keeping xmlns:* attributes on response nodes
  delete_namespace_attributes false

  # Savon defaults to nil for empty XML tags (set to "" for an empty String)
  empty_tag_value nil

  # Savon defaults to converting dashes in response tag names to underscores
  convert_dashes_to_underscores true

  # Savon defaults to scrubbing invalid byte sequences from the response body
  scrub_xml true
end

client.call(:operation) do
  # Savon defaults to activate "advanced typecasting"
  advanced_typecasting true

  # Savon defaults to the Nokogiri parser
  response_parser :nokogiri
end

These options map to Nori’s options and you can find more information about how they work in the README.

#to_xml

Returns the raw SOAP response. #to_s is an alias for the same value.

response.to_xml  # => "<response><success>true</success><name>luke</name></response>"
response.to_s    # same

#doc

Returns the SOAP response as a Nokogiri document.

response.doc  # => #<Nokogiri::XML::Document:0x1017b4268 ...

#xpath

Delegates to Nokogiri’s xpath method.

response.xpath("//v1:authenticateResponse/return/success").first.inner_text.should == "true"

#multipart?

Returns true when the response Content-Type header starts with multipart.

response.multipart?  # => true

#attachments

For multipart (MTOM) responses, returns the non-SOAP MIME parts as an Array of Mail::Part objects. Returns [] when the response isn’t multipart.

response.attachments.each do |part|
  File.binwrite("downloads/#{part.filename}", part.body.decoded)
end

#http

Returns the underlying Savon::Transport::Response, which wraps the HTTP response from the configured adapter (HTTPI or Faraday). It exposes code, headers, body, and cookies for inspecting the transport-level response.

response.http          # => Savon::Transport::Response
response.http.code     # => 200
response.http.headers  # => { "Content-Type" => "text/xml" }
response.http.body     # => "<soap:Envelope>...</soap:Envelope>"

The shape of cookies depends on the transport. The HTTPI transport returns an Array of HTTPI::Cookie objects, the Faraday transport returns a Hash of cookie names to values. Both shapes can be passed back to the next request via the cookies local.

response.http.cookies  # => [#<HTTPI::Cookie ...>]      with transport: :httpi
response.http.cookies  # => { "token" => "secret" }     with transport: :faraday

In case you disabled the global :raise_errors option, you can ask the response for its state.

response.success?     # => false
response.soap_fault?  # => true
response.http_error?  # => false

#successful? is an alias for #success?.

#soap_fault

Returns the Savon::SOAPFault for the response, or nil when no SOAP fault occurred. Useful as a conditional when raise_errors is disabled:

if fault = response.soap_fault
  logger.warn "SOAP fault: #{fault.to_hash[:fault][:faultstring]}"
end

When raise_errors is enabled (the default), Savon raises the fault instead of returning it on the response.

#http_error

Returns the Savon::HTTPError for the response, or nil when the HTTP layer returned a successful status. Useful as a conditional when raise_errors is disabled:

if error = response.http_error
  logger.warn "HTTP error: #{error.to_s}"
end

When raise_errors is enabled (the default), Savon raises the error instead of returning it on the response.