| Name |
Space |
Section |
Page |
Version |
Status |
Reviewed |
Author(s) |
| Making a SOAP Controller with Optio's Soaplib |
Pylons Cookbook |
Controllers |
Making a SOAP Controller with Optio's Soaplib |
1.0 |
Draft |
False |
Ben Bangert |
Introduction
Implementing SOAP in an application used to be hard work but now, thanks to Optio's Soaplib
WSGI app, the hard work has been done for you.
Install soaplib
 |
You will need to install Routes 1.7 version for soaplib to function properly behind Pylons.
1 | sudo easy_install -U Routes
|
|
1 | sudo easy_install soaplib
|
Soaplib should now be installed and ready to use.
Create a SOAP Controller
Next, create a basic controller to get started with:
Now that we have the controller stub file we need to modify it to use soaplib. In this example, we'll use the basic HelloWorld SOAP service. The Pylons application in this example is named 'wsgitest', make sure to change that line to reflect the name of your Pylons app.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | from wsgitest.lib.base import *
from soaplib.wsgi_soap import SimpleWSGISoapApp
from soaplib.service import soapmethod
from soaplib.serializers.primitive import String, Integer, Array
class HelloWorldService(SimpleWSGISoapApp):
@soapmethod(String,Integer,_returns=Array(String))
def say_hello(self,name,times):
results = []
for i in range(0,times):
results.append('Hello, %s'%name)
return results
SoapyController = HelloWorldService()
|
Modify Routes To Download WSDL
Insert the following line
1 | map.connect(':(controller).wsdl')
|
before
1 | map.connect(':controller/:action/:id')
|
in config/routing.py so that make_map looks like
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | def make_map():
"""Create, configure and return the routes Mapper"""
map = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])
# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
map.connect('error/:action/:id', controller='error')
# CUSTOM ROUTES HERE
map.connect(':(controller).wsdl')
map.connect(':controller/:action/:id')
return map
|
This will allow the wsdl to be downloaded using the url
http://localhost:5000/soapy.wsdl
Test the SOAP Service
Open a python prompt in your projects directory, and run the following code (replacing wsgitest with the name of your project):
1
2
3
4
5 | >>> from soaplib.client import make_service_client
>>> from wsgitest.controllers.soapy import HelloWorldService
>>> client = make_service_client('http://localhost:5000/soapy',HelloWorldService())
>>> print client.say_hello("Dave",5)
['Hello, Dave', 'Hello, Dave', 'Hello, Dave', 'Hello, Dave', 'Hello, Dave']
|
Testing the built-in WSDL facility
As above, run the following code (replacing wsgitest with the name of your project):
1
2
3
4 | >>> from soaplib.client import make_service_client
>>> from wsgitest.controllers.soapy import HelloWorldService
>>> client = make_service_client('http://localhost:5000/soapy',HelloWorldService())
>>> client.server.wsdl('')
|
The result is pretty-printed below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 | <?xml version='1.0' encoding='utf-8'?>
<definitions
name="HelloWorldService"
targetNamespace="wsgitest.controllers.soapy.HelloWorldService"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="wsgitest.controllers.soapy.HelloWorldService"
xmlns:typens="wsgitest.controllers.soapy.HelloWorldService"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<types>
<schema
targetNamespace="wsgitest.controllers.soapy.HelloWorldService"
xmlns="http://www.w3.org/2001/XMLSchema">
<xs:element name="say_hello" type="tns:say_hello" />
<xs:complexType name="say_hello">
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="times" type="xs:int" />
</xs:sequence>
</xs:complexType>
<xs:element name="stringArray" type="tns:stringArray" />
<xs:complexType name="say_helloResponse">
<xs:sequence>
<xs:element name="retval" type="tns:stringArray" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="stringArray">
<xs:sequence>
<xs:element maxOccurs="unbounded"
minOccurs="0" name="string" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="say_helloResponse" type="tns:say_helloResponse" />
</schema>
</types>
<message name="say_hello">
<part element="tns:say_hello" name="say_hello" />
</message>
<message name="say_helloResponse">
<part element="tns:say_helloResponse" name="say_helloResponse" />
</message>
<portType name="HelloWorldService">
<operation name="say_hello" parameterOrder="say_hello">
<documentation>
Docstrings for service methods appear in the wsdl output.
</documentation>
<input message="tns:say_hello" name="say_hello" />
<output message="tns:say_helloResponse" name="say_helloResponse" />
</operation>
</portType>
<plnk:partnerLinkType name="HelloWorldService">
<plnk:role name="HelloWorldService">
<plnk:portType name="tns:HelloWorldService" />
</plnk:role>
</plnk:partnerLinkType>
<binding name="HelloWorldService" type="tns:HelloWorldService">
<soap:binding
style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="say_hello">
<soap:operation soapAction="say_hello" style="document" />
<input name="say_hello">
<soap:body use="literal" />
</input>
<output name="say_helloResponse">
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="HelloWorldService">
<port binding="tns:HelloWorldService" name="HelloWorldService">
<soap:address location="" />
</port>
</service>
</definitions>
|
Server-side example fragment
The rest of the work consists of adding functionality to the controller. As a simple illustration, here is a fragment from one of the soaplib examples:
1
2
3
4
5
6
7
8 | @soapmethod(User,_returns=Integer)
def add_user(self,user):
global user_database
global userid_seq
user.userid = userid_seq
userid_seq = userid_seq+1
user_database[user.userid] = user
return user.userid
|
Conclusion
Adding additional SOAP controllers, or increasing the complexity of the existing one is rather easy and can be done as the examples on the optio
website show. In addition, it's rather trivial to use the existing database functions and other Pylons features you might desire in your SOAP services.