Just for the sake of seeing it. Sometimes when you don’t know how something works you may have some ideas that take you away from how it actually works. And how simple it is. This might be the case with a SOAP service.
SOAP Server
# app.rb
require 'sinatra'
require 'sinatra/namespace'
require 'savon'
namespace '/soap' do
post '/service' do
builder = Nokogiri::XML::Builder.new do |xml|
xml.Envelope xmlns: "http://schemas.xmlsoap.org/soap/envelope/" do
xml.Body do
xml.myResponse do
xml.result "Hello from SOAP Server!"
end
end
end
end
content_type 'text/xml'
builder.to_xml
end
get '/wsdl' do
content_type 'text/xml'
send_file 'service.wsdl'
end
end
Now the client needs to do two things. First is knowing what is available. So it calls the /wsdl
endpoint which stands for Web Services Description Language. It provides the client with the description of the service. Here is how it may look like:
# service.wsdl
<?xml version="1.0"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://example.com/soap/service" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MyService" targetNamespace="http://example.com/soap/service">
<types>
<!-- Define your data types here -->
</types>
<message name="MyRequest">
<part name="parameters" type="xsd:string"/>
</message>
<message name="MyResponse">
<part name="result" type="xsd:string"/>
</message>
<portType name="MyServicePortType">
<operation name="MyOperation">
<input message="tns:MyRequest"/>
<output message="tns:MyResponse"/>
</operation>
</portType>
<binding name="MyServiceBinding" type="tns:MyServicePortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="MyOperation">
<soap:operation soapAction="http://example.com/MyOperation"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="MyService">
<port name="MyServicePort" binding="tns:MyServiceBinding">
<soap:address location="http://localhost:4567/soap/service"/>
</port>
</service>
</definitions>
It’s bulky but this is what SOAP services are known for. You may want to read this Wikipedia page to get to know more about WSDL.
SOAP Client
require 'savon'
client = Savon.client(wsdl: 'http://localhost:4567/soap/wsdl')
puts "Available operations:"
puts client.operations
operation = :my_operation # This should match the operation name defined in your WSDL
message = { parameters: 'test' }
response = client.call(operation, message: message)
puts "Response:"
puts response.to_hash
# Available operations:
# my_operation
# Response:
# {:my_response=>{:result=>"Hello from SOAP Server!"}}
And if you try some other operation name you’ll get:
Unable to find SOAP operation: :some_other_operation (Savon::UnknownOperationError)
Operations provided by your service: [:my_operation]