referencia: (Gracias a Doug Hellmann) building soap service
El Zolera Jabón infrastucture (ZSI), es una parte del proyecto pywebsvcs. Proporciona bibliotecas completas de servidor y cliente para trabajar con SOAP. Para usarlo, un desarrollador escribe el archivo WSDL (a mano o usando un editor WSDL), y luego genera la fuente de Python para el cliente y los stubs para el servidor. Las estructuras de datos definidas en el archivo WSDL se convierten en clases de Python que se pueden usar tanto en el código del cliente como en el del servidor.
implementamos un servicio echo simple que devuelve como salida lo que sea que obtenga como entrada del cliente. El Listado 1 contiene las entradas WSDL hechas a mano para la versión ZSI de este servicio.
Listado 1
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="urn:ZSI"
targetNamespace="urn:ZSI" >
<types>
<xsd:schema elementFormDefault="qualified"
targetNamespace="urn:ZSI">
<xsd:element name="Echo">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:anyType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</types>
<message name="EchoRequest">
<part name="parameters" element="tns:Echo" />
</message>
<message name="EchoResponse">
<part name="parameters" element="tns:Echo"/>
</message>
<portType name="EchoServer">
<operation name="Echo">
<input message="tns:EchoRequest"/>
<output message="tns:EchoResponse"/>
</operation>
</portType>
<binding name="EchoServer" type="tns:EchoServer">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="Echo">
<soap:operation soapAction="Echo"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="EchoServer">
<port name="EchoServer" binding="tns:EchoServer">
<soap:address location="http://localhost:7000"/>
</port>
</service>
</definitions>
para generar el código de cliente y servidor desde el WSDL, se alimentan en el programa de wsdl2py (incluido con ZSI). Para agregar soporte para tipos complejos, agregue la opción -b, pero no es necesario para este ejemplo simple. wsdl2py será, en respuesta, producen tres archivos:
Listado 2
EchoServer_client.py es el código necesario para construir un cliente para el servicio web SimpleEcho.
##################################################
# file: EchoServer_client.py
#
# client stubs generated by
# "ZSI.generate.wsdl2python.WriteServiceModule"
#
##################################################
from EchoServer_types import *
import urlparse, types
from ZSI.TCcompound import ComplexType, Struct
from ZSI import client
from ZSI.schema import GED, GTD
import ZSI
from ZSI.generate.pyclass import pyclass_type
# Locator
class EchoServerLocator:
EchoServer_address = "http://localhost:7000"
def getEchoServerAddress(self):
return EchoServerLocator.EchoServer_address
def getEchoServer(self, url=None, **kw):
return EchoServerSOAP(
url or EchoServerLocator.EchoServer_address,
**kw)
# Methods
class EchoServerSOAP:
def __init__(self, url, **kw):
kw.setdefault("readerclass", None)
kw.setdefault("writerclass", None)
# no resource properties
self.binding = client.Binding(url=url, **kw)
# no ws-addressing
# op: Echo
def Echo(self, request, **kw):
if isinstance(request, EchoRequest) is False:
raise TypeError, "%s incorrect request type" % \
(request.__class__)
# no input wsaction
self.binding.Send(None, None, request, soapaction="Echo", **kw)
# no output wsaction
response = self.binding.Receive(EchoResponse.typecode)
return response
EchoRequest = GED("urn:ZSI", "Echo").pyclass
EchoResponse = GED("urn:ZSI", "Echo").pyclass
Listado 3
EchoServer_server.py contiene el código necesario para construir el servidor de servicios web SimpleEcho.
##################################################
# file: EchoServer_server.py
#
# skeleton generated by
# "ZSI.generate.wsdl2dispatch.ServiceModuleWriter"
#
##################################################
from ZSI.schema import GED, GTD
from ZSI.TCcompound import ComplexType, Struct
from EchoServer_types import *
from ZSI.ServiceContainer import ServiceSOAPBinding
# Messages
EchoRequest = GED("urn:ZSI", "Echo").pyclass
EchoResponse = GED("urn:ZSI", "Echo").pyclass
# Service Skeletons
class EchoServer(ServiceSOAPBinding):
soapAction = {}
root = {}
def __init__(self, post='', **kw):
ServiceSOAPBinding.__init__(self, post)
def soap_Echo(self, ps, **kw):
request = ps.Parse(EchoRequest.typecode)
return request,EchoResponse()
soapAction['Echo'] = 'soap_Echo'
root[(EchoRequest.typecode.nspname,EchoRequest.typecode.pname)] = \
'soap_Echo'
Listado 4
EchoServer_types.py tiene el tipo definiciones utilizadas por el cliente y el código del servidor.
##################################################
# file: EchoServer_types.py
#
# schema types generated by
# "ZSI.generate.wsdl2python.WriteServiceModule"
#
##################################################
import ZSI
import ZSI.TCcompound
from ZSI.schema import (LocalElementDeclaration, ElementDeclaration,
TypeDefinition, GTD, GED)
from ZSI.generate.pyclass import pyclass_type
##############################
# targetNamespace
# urn:ZSI
##############################
class ns0:
targetNamespace = "urn:ZSI"
class Echo_Dec(ZSI.TCcompound.ComplexType, ElementDeclaration):
literal = "Echo"
schema = "urn:ZSI"
def __init__(self, **kw):
ns = ns0.Echo_Dec.schema
TClist = [ZSI.TC.AnyType(pname=(ns,"value"),
aname="_value", minOccurs=1, maxOccurs=1,
nillable=False, typed=False,
encoded=kw.get("encoded"))]
kw["pname"] = ("urn:ZSI","Echo")
kw["aname"] = "_Echo"
self.attribute_typecode_dict = {}
ZSI.TCcompound.ComplexType.__init__(self,None,TClist,
inorder=0,**kw)
class Holder:
__metaclass__ = pyclass_type
typecode = self
def __init__(self):
# pyclass
self._value = None
return
Holder.__name__ = "Echo_Holder"
self.pyclass = Holder
# end class ns0 (tns: urn:ZSI)
Una vez generado, estos archivos no están destinados a ser editado, porque van a ser regenerados como parte de un proceso de construcción cada vez que cambia de entrada WSDL. El código en los archivos crece a medida que se agregan más tipos y llamadas a la definición del servicio.
La implementación del servidor va en un archivo separado que importa el código generado. En el ejemplo, el servicio real está en las líneas 18-25 del Listado 5. El decorador @soapmethod define la entrada (una EchoRequest) y la salida (una EchoResponse) para la llamada. En el ejemplo, la implementación de soap_Echo() simplemente completa el valor de respuesta con el valor de solicitud y devuelve tanto la solicitud como la respuesta. A partir de ahí, ZSI se encarga de construir la respuesta SOAP y enviarla de vuelta al cliente.
Listing 5
import os
import sys
from EchoServer_client import *
from ZSI.twisted.wsgi import (SOAPApplication,
soapmethod,
SOAPHandlerChainFactory)
class EchoService(SOAPApplication):
factory = SOAPHandlerChainFactory
wsdl_content = dict(name='Echo',
targetNamespace='urn:echo',
imports=(),
portType='',
)
def __call__(self, env, start_response):
self.env = env
return SOAPApplication.__call__(self, env, start_response)
@soapmethod(EchoRequest.typecode,
EchoResponse.typecode,
operation='Echo',
soapaction='Echo')
def soap_Echo(self, request, response, **kw):
# Just return what was sent
response.Value = request.Value
return request, response
def main():
from wsgiref.simple_server import make_server
from ZSI.twisted.wsgi import WSGIApplication
application = WSGIApplication()
httpd = make_server('', 7000, application)
application['echo'] = EchoService()
print "listening..."
httpd.serve_forever()
if __name__ == '__main__':
main()
Listado 6 incluye una muestra de cómo utilizar las bibliotecas de cliente ZSI para acceder a los servidores desde el cliente final. Todo lo que se necesita hacer es crear un identificador para el servicio web EchoServer, crear un EchoRequest, enviarlo al servicio web y leer la respuesta.
from EchoServer_client import *
import sys, time
loc = EchoServerLocator()
port = loc.getEchoServer(url='http://localhost:7000/echo')
print "Echo: ",
msg = EchoRequest()
msg.Value = "Is there an echo in here?"
rsp = port.Echo(msg)
print rsp.Value