November 15, 2008

Python Web Services - HOW TO 1 - ZSI Server

It’s possible to write web services with Python in too many ways. For example you can use Python-ZSI framework or SOAPpy or Python XML-RPC. But if you don't want to face with the problem which some of your customers whom use C# wants a wsdl file to create a connection object, i suggest you to use Python-ZSI framework and here you can find how to write down it in Linux.

First we need to install zsi package to our Linux distribution;

You can get the package from here. Also the package can be in your distribution’s repository’s. So you can check it first. But i strongly recommend you to install the package manually. To install the package manually you'll just do after downloading the package;

tar -xvzf zsi.tar.gz

cd zsi

python setup.py build

python setup.py install

Now we are starting to go for web service, (is it always been a hello world example, what a draaaag. so i write down a sillier one, that takes “hello world” as a parameter and sends it back);

First we start to write down our .wsdl file. If you are not familiar with .wsdl standarts, you can check here.

Now here it comes, our wsdl definitions;

<?xml version="1.0"?>

<definitions name="SillyWebService"

targetNamespace="http://services.zsiserver.net:8181/SillyWebService"

xmlns:tns="http://services.zsiserver.net:8181/SillyWebService"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

xmlns="http://schemas.xmlsoap.org/wsdl/">

That “http://services.zsiserver.net:8181” is equivalent to your “http://localhost:8181” and your “http://yourip:8181”. With ZSI the web services are connecting through the internet via a port. And if you have any web server running on 8080 like Apache, you must change the web services port. So i give 8181 to the port number.

And now the major elements of our wsdl;

<message name="sillyfunctionRequest">

<part name="param" type="xsd:string"/>

</message>

<message name="sillyfunctionResponse">

<part name="return" type="xsd:string"/>

</message>

Like message; which represents an abstract definition of the data being transmitted, like type; which provides data type definitions used to describe the messages exchanged.

<portType name="SillyWebServicePortType">

<operation name="sillyfunction">

<documentation> A Silly Web Service Function </documentation>

<input message="tns:sillyfunctionRequest"/>

<output message="tns:sillyfunctionResponse"/>

</operation>

</portType>

Like portType; which is a set of abstract operations.

<binding name="SillyWebServiceBinding" type="tns:SillyWebServicePortType">

<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

<operation name="sillyfunction">

<soap:operation soapAction="http://services.zsiserver.net:8181/SillyWebService/sillyfunction"/>

<input>

<soap:body use="literal" namespace="http://services.zsiserver.net:8181/SillyWebService"/>

</input>

<output>

<soap:body use="literal" namespace="http://services.zsiserver.net:8181/SillyWebService"/>

</output>

</operation>

</binding>

Like binding; which specifies concrete protocol and data format specifications for the operations and messages defined by a particular portType.

<service name="SillyWebService">

<documentation> A Silly Web Service Implementation </documentation>

<port name="SillyWebServicePort" binding="tns:SillyWebServiceBinding">

<soap:address location="http://services.zsiserver.net:8181/SillyWebService"/>

</port>

</service>

Like service; which is used to aggregate a set of related ports, like port; which specifies an address for a binding, thus defining a single communication endpoint.

I don’t mention much about types, messages etc in here, because we aren’t learning wsdl, aren’t we.

And at the end we don't forget to close definitions tag.

</definitions>

After we finished about writing down our wsdl file, we use wsdl2py from console to create the web service objects with ZSI. I assume we save our wsdl file as “silly.wsdl”;

wsdl2py silly.wsdl

After this command we must now see that we have 3 files that are automatically created by ZSI.

SillyWebService_server.py

SillyWebService_client.py

SillyWebService_types.py

After that, it's time to write down our python web service code or to arrange our code that we wrote down before.

First we import the modules that we need to run our web service;

from optparse import OptionParser

from ZSI.wstools import logging

from ZSI.ServiceContainer import AsServer

from SillyWebService_server import SillyWebService

Then we start to write our web service implementation, that handles the requests and return responses;

class WebServiceImplementation(SillyWebService):

_wsdl = "".join(open("silly.wsdl").readlines())

def soap_sillyfunction(self, ps, **kw):

request, response = SillyWebService.soap_sillyfunction(self, ps, **kw)

response._return = self.sillyfunction(request._param)

return request, response

def sillyfunction(self, param):

return param

The first function that starts with "soap_" is to to interact with the ZSI framework. And the second function is our implementation function. We handle the request and give response on this function.

At the end we write down the parts that run the web service as a server via a port;

op = OptionParser(usage="%prog [options]")

op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)", metavar="LOGLEVEL")

op.add_option("-p", "--port", help="HTTP port", metavar="PORT", default=8181, type="int")

options, args = op.parse_args()

if options.loglevel:

loglevel = eval(options.loglevel, logging.__dict__)

logger = logging.getLogger("")

logger.setLevel(loglevel)

AsServer(port=options.port, services=[WebServiceImplementation()])

And that's the time for us to run our web service, i assume we save our web service code as silly.py;

python silly.py

if you want to run the web service behind, you must add '&' after the command;

python silly.py&

And here it is the sample code. Also if you want to look how to write down a client for this web service in python, you can look here.

No comments: