First, we'll look at the SOAP URL and see what Prefixes
, Global Elements
, Global Types
, Bindings
, and Services
are provided by the SOAP Service.
You can do this by running zeep as a CLI tool.
export WSDL_URL="http://www.dneonline.com/calculator.asmx?WSDL"
python -m zeep $WSDL_URL
This should return and print a lot of data to the screen. We'll walk through it using Python after examining it. Here is what should appear:
Prefixes:
xsd: http://www.w3.org/2001/XMLSchema
ns0: http://tempuri.org/
Global elements:
ns0:Add(intA: xsd:int, intB: xsd:int)
ns0:AddResponse(AddResult: xsd:int)
ns0:Divide(intA: xsd:int, intB: xsd:int)
ns0:DivideResponse(DivideResult: xsd:int)
ns0:Multiply(intA: xsd:int, intB: xsd:int)
ns0:MultiplyResponse(MultiplyResult: xsd:int)
ns0:Subtract(intA: xsd:int, intB: xsd:int)
ns0:SubtractResponse(SubtractResult: xsd:int)
Global types:
xsd:anyType
xsd:ENTITIES
xsd:ENTITY
xsd:ID
xsd:IDREF
xsd:IDREFS
xsd:NCName
xsd:NMTOKEN
xsd:NMTOKENS
xsd:NOTATION
xsd:Name
xsd:QName
xsd:anySimpleType
xsd:anyURI
xsd:base64Binary
xsd:boolean
xsd:byte
xsd:date
xsd:dateTime
xsd:decimal
xsd:double
xsd:duration
xsd:float
xsd:gDay
xsd:gMonth
xsd:gMonthDay
xsd:gYear
xsd:gYearMonth
xsd:hexBinary
xsd:int
xsd:integer
xsd:language
xsd:long
xsd:negativeInteger
xsd:nonNegativeInteger
xsd:nonPositiveInteger
xsd:normalizedString
xsd:positiveInteger
xsd:short
xsd:string
xsd:time
xsd:token
xsd:unsignedByte
xsd:unsignedInt
xsd:unsignedLong
xsd:unsignedShort
Bindings:
Soap11Binding: {http://tempuri.org/}CalculatorSoap
Soap12Binding: {http://tempuri.org/}CalculatorSoap12
Service: Calculator
Port: CalculatorSoap (Soap11Binding: {http://tempuri.org/}CalculatorSoap)
Operations:
Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
Port: CalculatorSoap12 (Soap12Binding: {http://tempuri.org/}CalculatorSoap12)
Operations:
Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
Let's take a look at this and go through each part.
The top, first part, Prefixes
.
Prefixes:
xsd: http://www.w3.org/2001/XMLSchema
ns0: http://tempuri.org/
These are Schemas and Name spaces that provide a common structure and if any custom elements are added, they're in a name space so they don't overwrite the default stuff.
The next part is Global Elements
.
Global elements:
ns0:Add(intA: xsd:int, intB: xsd:int)
ns0:AddResponse(AddResult: xsd:int)
ns0:Divide(intA: xsd:int, intB: xsd:int)
ns0:DivideResponse(DivideResult: xsd:int)
ns0:Multiply(intA: xsd:int, intB: xsd:int)
ns0:MultiplyResponse(MultiplyResult: xsd:int)
ns0:Subtract(intA: xsd:int, intB: xsd:int)
ns0:SubtractResponse(SubtractResult: xsd:int)
These show you name space and Service
functions available. If there was a second service or a version 2, it would most likely show the same service, just in a different namespace.
The Global Types
section.
Global types:
xsd:anyType
xsd:ENTITIES
xsd:ENTITY
xsd:ID
xsd:IDREF
xsd:IDREFS
xsd:NCName
xsd:NMTOKEN
xsd:NMTOKENS
xsd:NOTATION
xsd:Name
xsd:QName
xsd:anySimpleType
xsd:anyURI
xsd:base64Binary
xsd:boolean
xsd:byte
xsd:date
xsd:dateTime
xsd:decimal
xsd:double
xsd:duration
xsd:float
xsd:gDay
xsd:gMonth
xsd:gMonthDay
xsd:gYear
xsd:gYearMonth
xsd:hexBinary
xsd:int
xsd:integer
xsd:language
xsd:long
xsd:negativeInteger
xsd:nonNegativeInteger
xsd:nonPositiveInteger
xsd:normalizedString
xsd:positiveInteger
xsd:short
xsd:string
xsd:time
xsd:token
xsd:unsignedByte
xsd:unsignedInt
xsd:unsignedLong
xsd:unsignedShort
This part shows all the different types that are supported by the WSDL and the remote SOAP service. Most of these will be supported by the SOAP Service and your programming language.
The Bindings
.
Bindings:
Soap11Binding: {http://tempuri.org/}CalculatorSoap
Soap12Binding: {http://tempuri.org/}CalculatorSoap12
These Bindings
allow you to specify different Ports, or versions of the Service we're wanting to use.
Now we get to the main fun part, interacting with the Service
.
Service: Calculator
Port: CalculatorSoap (Soap11Binding: {http://tempuri.org/}CalculatorSoap)
Operations:
Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
Port: CalculatorSoap12 (Soap12Binding: {http://tempuri.org/}CalculatorSoap12)
Operations:
Add(intA: xsd:int, intB: xsd:int) -> AddResult: xsd:int
Divide(intA: xsd:int, intB: xsd:int) -> DivideResult: xsd:int
Multiply(intA: xsd:int, intB: xsd:int) -> MultiplyResult: xsd:int
Subtract(intA: xsd:int, intB: xsd:int) -> SubtractResult: xsd:int
Here we have a Service
named Calculator with two Ports
. For now we won't worry about which port we use, but we can bind to specific ones if we desire so. We'll simply call this by Calculator
in our application.
Using the SOAP WSDL Endpoint we just used, we'll do a simple action, then walk through the objects.
#!/usr/bin/env python
"""Python Zeep Client Example
"""
import os
import zeep
def main():
"""Main
"""
wsdl_url = os.environ.get('WSDL_URL')
soap = zeep.Client(wsdl=wsdl_url,
service_name="Calculator",
port_name="CalculatorSoap12")
result = soap.service.Add(5, 5)
assert result == 10
if __name__ == '__main__':
main()
This script should return an integer with our two numbers added together. We've specified a few parameters to our zeep.Client
function. First was our wsdl=
. This is the URL to the WSDL document we're wanting to use. The service_name=
is denoted by the Services listed in the output or in the WSDL we're using. The port_name=
is also part of the Services listed in the WSDL or output from the previous command. We make our client, then we use soap.service.Add(5, 5)
. How does the program know to use that Add()
? It's because we specified which Service, and Port to bind to which provides that function. You may notice there are two different Ports. One is a SOAP 11 and one is a SOAP 12 port as kind of denoted by the name of the Port.
How could one programmatically go through this? We'll use Python's dir()
function. To my mind at first, this meant, directory. However, it's used to see what attributes are available to an object in Python. These objects are typically returned in an Array so you could iterate through that list and see what's available.
Let's walk through a few objects. Start up the Python REPL tool.
python
Now we can start our journey.
import os
import zeep
soap = zeep.Client(wsdl=os.environ.get('WSDL_URL'))
dir(soap)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_default_port_name', '_default_service', '_default_service_name', '_default_soapheaders', '_get_port', '_get_service', 'bind', 'create_message', 'create_service', 'get_element', 'get_type', 'namespaces', 'plugins', 'service', 'set_default_soapheaders', 'set_ns_prefix', 'settings', 'transport', 'type_factory', 'wsdl', 'wsse']
The parts we're really interested in are the ones that don't contain __
in them. So let's start with the wsdl
attribute.
dir(soap.wsdl)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_add_definition', '_definitions', '_get_xml_document', 'bindings', 'dump', 'location', 'messages', 'port_types', 'services', 'settings', 'transport', 'types']
Here we can start to see how Python presents the WSDL we're using. Since no one, literally NO ONE, likes working with XML, it's presented as Python primitives. Let's checkout the soap.wsdl.services
since we're working with Services above.
dir(soap.wsdl.services)
['__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'move_to_end', 'pop', 'popitem', 'setdefault', 'update', 'values']
Now, you can see what items
are available in the Services. You can also view the keys
as well.
soap.wsdl.services.keys()
odict_keys(['Calculator'])
soap.wsdl.services.items()
odict_items([('Calculator', <Service(name='Calculator', ports=OrderedDict([('CalculatorSoap', <Port(name='CalculatorSoap', binding=<Soap11Binding(name='{http://tempuri.org/}CalculatorSoap', port_type=<PortType(name='{http://tempuri.org/}CalculatorSoap')>)>, {'address': 'http://www.dneonline.com/calculator.asmx'})>), ('CalculatorSoap12', <Port(name='CalculatorSoap12', binding=<Soap12Binding(name='{http://tempuri.org/}CalculatorSoap12', port_type=<PortType(name='{http://tempuri.org/}CalculatorSoap')>)>, {'address': 'http://www.dneonline.com/calculator.asmx'})>)]))>)])
This shows us a few things, but isn't super helpful all the time. Let's walk through the soap.service
object.
dir(soap.service)
['Add', 'Divide', 'Multiply', 'Subtract', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__self_class__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__thisclass__']
Whoa! Look at that, we've got our Methods we're wanting. There's not much else to explore at this point. We know we can call soap.service.Add()
and pass it the parameters specified in the WSDL and the very first command output we made.