soaplib v2.0.0beta翻译Quick Start Examples

Hello World

这个例子使用soaplib中简单的wsgi-webservice来部署此服务。


声明一个Soaplib Service

import soaplib
from soaplib.core.service import rpc, DefinitionBase
from soaplib.core.model.primitive import String, Integer
from soaplib.core.server import wsgi
from soaplib.core.model.clazz import Array


class HelloWorldService(DefinitionBase):
    @soap(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

if __name__=='__main__':
    try:
        from wsgiref.simple_server import make_server
        soap_application = soaplib.core.Application([HelloWorldService], 'tns')
        wsgi_application = wsgi.Application(soap_application)
        server = make_server('localhost', 7789, wsgi_application)
        server.serve_forever()
    except ImportError:
        print "Error: example server code requires Python >= 2.5"

解析这个示例:DefinitionBase是所有soap服务的基类

from soaplib.core.service import DefinitionBase

rpc装饰器暴露方法名作为soap的调用方法,并且声明它所接收和返回的数据类型

from soaplib.core.service import rpc

导入该方法的模型(稍后会有更多关于该模型的介绍)

from soaplib.core.model.primitive import String, Integer
from soaplib.core.model.clazz import Array

创建一个可以作为WSGI应用程序部署的soap服务的简单方法是:扩展DefinitionBase

class HelloWorldService(DefinitionBase):

rpc装饰器将每个方法标记为soap方法,并定义soap参数的类型,顺序,以及返回值。下面代码定义的方法接受一个String,一个Integer并返回一个String数组->Array(String)

@soap(String,Integer,_returns=Array(String))

方法本身没有任何特殊之处,所有输入的变量和返回类型都是标准的python对象。

def say_hello(self,name,times):
    results = []
    for i in range(0,times):
        results.append('Hello, %s'%name)
    return results

部署服务

soaplib已经用其他的几个web服务测试过,这个例子使用简单的wsgi web服务,任何与WSGI兼容的服务器都可以运行。

if __name__=='__main__':
    try:
        from wsgiref.simple_server import make_server
        soap_application = soaplib.core.Application([HelloWorldService], 'tns')
        wsgi_application = wsgi.Application(soap_application)
        server = make_server('localhost', 7789, wsgi_application)
        server.serve_forever()
    except ImportError:
        print "Error: example server code requires Python >= 2.5"

调用这个服务

>>> from suds.client import Client
>>> hello_client = Client('http://localhost:7789/?wsdl')
>>> result = hello_client.service.say_hello("Dave", 5)
>>> print result

(stringArray){
   string[] =
      "Hello, Dave",
      "Hello, Dave",
      "Hello, Dave",
      "Hello, Dave",
      "Hello, Dave",
 }

suds是一个单独的项目,用于在python中构建轻量级的soap客户端。想要了解更多信息,请访问该项目的网页:
https://fedorahosted.org/suds/


用户管理

让我们来试一下比字符串和整型作为参数更复杂的例子!下面是一个使用了复杂嵌套数据类型但极其简单的例子。

from soaplib.core import Application
from soaplib.core.server import wsgi
from soaplib.core.service import soap
from soaplib.core.service import DefinitionBase
from soaplib.core.model.primitive import String, Integer
from soaplib.core.model.clazz import ClassModel, Array

user_database = {}
userid_seq = 1

class Permission(ClassModel):
        application = String
        feature = String

class User(ClassModel):
        userid = Integer
        username = String
        firstname = String
        lastname = String
        permissions = Array(Permission)

class UserManager(DefinitionBase):
        @soap(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

        @soap(Integer,_returns=User)
        def get_user(self,userid):
                global user_database
                return user_database[userid]

        @soap(User)
        def modify_user(self,user):
                global user_database
                user_database[user.userid] = user

        @soap(Integer)
        def delete_user(self,userid):
                global user_database
                del user_database[userid]

        @soap(_returns=Array(User))
        def list_users(self):
                global user_database
                return [v for k,v in user_database.items()]

if __name__=='__main__':
        from wsgiref.simple_server import make_server
        soap_app = Application([UserManager], 'tns')
        wsgi_app = wsgi.Application(soap_app)

        server = make_server('localhost', 7789, wsgi_app)
        server.serve_forever()

让我们跳到新东西的地方:

class Permission(ClassModel):
        application = String
        feature = String

class User(ClassModel):
        userid = Integer
        username = String
        firstname = String
        lastname = String
        permissions = Array(Permission)

例子中的Permission和User结构是标准的python对ClassModel扩展的对象。SoapLib将ClassModel用作一般类型,扩展后将生成可在SOAP服务中使用的复杂可序列化类型。


模型

在Soaplib中,将单独的参数转化组成xml,并提供构建wsdl的必要的信息。Soaplib有许多内置类型,这些内置类型为你提供大多数一般所必需的通用数据类型。


Primitives(基元)

基本的基元类型包括:String,Integer,DateTime,Null,Float,Boolean.
这些事soaplib.core中最基本的块。

>>> from soaplib.core.model.primitive import String
>>> from lxml import etree
>>> parent = etree.Element("parent")
>>> String.to_parent_element("abcd", "tns", parent)
>>> string_element = parent.getchildren()[0]
>>> print etree.tostring(string_element)
<ns0:retval xmlns:ns0="tns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">abcd</ns0:retval>
>>> print String.from_xml(string_element)
abcd
>>> String.get_type_name()
'string'
>>> String.get_type_name_ns()
'xs:string'

Array(数组)

soaplib中唯一可用的集合类型是Array(数组)类型。与基元类型不同,Arrays需要使用合适的内置类型进行实例化,因此他们可以完全(反)序列化数据。Arrays是同性质的,意味着他所保存的数据都是同一类型。对于混合类型和更多的动态数据,请使用Any类型。

>>> from soaplib.core.model.clazz import Array
>>> from soaplib.core.model.primitive import String
>>> from lxml import etree
>>> parent = etree.Element("parent")
>>> array_serializer = Array(String)
>>> array_serializer.to_parent_element(['a','b','c','d'], 'tns', parent)
>>> print etree.tostring(element)
<ns0:stringArray xmlns:ns0="tns"><ns1:string xmlns:ns1="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">a</ns1:string>
<ns2:string xmlns:ns2="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">b</ns2:string>
<ns3:string xmlns:ns3="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">c</ns3:string>
<ns4:string xmlns:ns4="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">d</ns4:string></ns0:stringArray>
>>> print array_serializer.from_xml(element)
['a', 'b', 'c', 'd']

Class(类)

ClassModel类型是用来定义和序列化复杂的嵌套结构。

>>> from soaplib.core.model.primitive import String, Integer
>>> from soaplib.core.model.clazz import ClassModel
>>> from lxml import etree
>>> class Permission(ClassModel):
...         __namespace__ = "permission"
...             application = String
...             feature = String
>>>
>>> class User(ClassModel):
...     __namespace__ = "user"
...             userid = Integer
...             username = String
...             firstname = String
...             lastname = String
...             permissions = Array(Permission)
>>>
>>> u = User()
>>> u.username = 'bill'
>>> u.permissions = []
>>> p = Permission()
>>> p.application = 'email'
>>> p.feature = 'send'
>>> u.permissions.append(p)
>>> parent = etree.Element('parenet')
>>> User.to_parent_element(u, 'tns', parent)
>>> element = parent[0]
>>> etree.tostring(element)
'<ns0:User xmlns:ns0="tns">
<ns1:username xmlns:ns1="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">bill</ns1:username>
<ns2:firstname xmlns:ns2="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<ns3:lastname xmlns:ns3="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<ns4:userid xmlns:ns4="None" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<ns5:permissions xmlns:ns5="None"><ns5:Permission><ns5:application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">email</ns5:application>
>>> User.from_xml(element).username
'bill'
>>>

Attachment(附件)

Attachment序列化程序被用于以base64编码字符串的形式传输二进制数据。在附件类中的数据可以被手动加载,或者从文件中读取。二进制数据的所有编码都将在被发送前完成,并在附件被接收后立即解码。

>>> from soaplib.core.model.binary import Attachment
>>> from lxml import etree
>>> a = Attachment(data='my binary data')
>>> parent = etree.Element('parent')
>>> Attachment.to_parent_element(a, "tns", parent )
>>> element = parent[0]
>>> print etree.tostring(element)
<ns0:retval xmlns:ns0="tns">bXkgYmluYXJ5IGRhdGE=
</ns0:retval>
>>> print Attachment.from_xml(element)
<soaplib.core.model.binary.Attachment object at 0x5c6d90>
>>> print Attachment.from_xml(element).data
my binary data
>>> a2 = Attachment(fileName='test.data') # load from file

Any(任意类型)

Any类型是用于传输非结构化xml数据的序列化程序。Any类型在处理动态数据中非常有用,并为使用soaplib.core传递数据提供了一个非常Python化的方式。Any序列化程序无法执行任何功能性任务,因为传入和返回的都是元素类型。Any类型最主要的目的在于声明它在WSDL中的存在。


AnyAsDict

AnyAsDict类型的作用和Any相同,除了它通过列表来正反序列化字典,而不是从一个原始的lxml.etree._Element对象来进行序列化。


Custom(自定义)

Soaplib提供一个非常简单的接口来自定义类型。只需要继承soaplib.core.model.base.Base并重写 from_xml(), to_parent_element()add_to_schema()
classmethods:

from soaplib.core.model.base import Base

class MySerializer(Base):
    @classmethod
    def to_parent_element(self,value,name='retval'):
        pass

    @classmethod
    def from_xml(self,element):
        pass

    @classmethod
    def add_to_schema(self,added_params):
        pass

二进制文件

在SOAP中,用来表现二进制数据最常见的方法是base64编码的字符串。Soaplib使用‘Attachment’序列化程序来处理所有二进制数据的编码和解码,并提供一些功能性函数来管理内存和磁盘中的二进制数据。

>>> from soaplib.core.model.binary import Attachment
>>> from lxml import etree
>>> a = Attachment(data="this is my binary data")
>>> parent = etree.Element("parent")
>>> Attachment.to_parent_element(a, "tns", parent)
>>> print et.tostring(parent)
<ns0:retval xmlns:ns0="tns">bXkgYmluYXJ5IGRhdGE=
</ns0:retval>
>>>

如果你想要返回一个二进制文件,很简单:

>>> from soaplib.core.model.binary import Attachment
>>> from lxml import etree as et
>>> a = Attachment(file_name="mydata")
>>> parent = etree.Element("parent")
>>> Attachment.to_parent_element(a, "tns", parent)
>>> print et.tostring(parent)
<ns0:retval xmlns="">dGhpcyBpcyBteSBiaW5hcnkgZGF0YQ==
</ns0:retval>
>>>

一个用于保存文档的服务示例:

from soaplib.core.service import rpc, DefinitionBase
from soaplib.core.model.primitive import String, Integer
from soaplib.core.model.clazz import Array
from soaplib.core.model.binary import Attachment
from soaplib.core.server import wsgi

from tempfile import mkstemp
import os

class DocumentArchiver(DefinitionBase):

    @soap(Attachment,_returns=String)
    def archive_document(self,document):
        '''
        This method accepts an Attachment object, and returns the filename of the
        archived file
        '''
        fd,fname = mkstemp()
        os.close(fd)

        document.file_name = fname
        document.save_to_file()

        return fname

    @soap(String,_returns=Attachment)
    def get_archived_document(self,file_path):
        '''
        This method loads a document from the specified file path
        and returns it.  If the path isn't found, an exception is
        raised.
        '''
        if not os.path.exists(file_path):
            raise Exception("File [%s] not found"%file_path)

        document = Attachment(file_name=file_path)
        # the service automatically loads the data from the file.
        # alternatively, The data could be manually loaded into memory
        # and loaded into the Attachment like:
        #   document = Attachment(data=data_from_file)
        return document



if __name__=='__main__':
    from wsgiref.simple_server import make_server
    soap_app = soaplib.core.Application([DocumentArchiver], 'tns')
    wsgi_app = wsgi.Application(soap_app)
    server = make_server('localhost', 7789, wsgi_app)
    server.serve_forever()

消息API

除了WSGI服务API之外,soaplib还提供一个通用的Message API,这个API允许你程序化构建SOAP消息的xml部分。这个消息类依附于 to_parent_element/from_xml 用作类型的用法模板。

>>> from soaplib.core.soap import Message
>>> from soaplib.core.model.primitive import String, Integer, Float
>>> import lxml.etree as et
>>> message = Message('myFunction',[('a',String),('b',Integer),('c',Float)])
>>> print et.tostring(message.to_parent_element('a',13,3.14))
<myFunction><a xmlns="" xsi:type="xs:string">a</a><b xmlns="" xsi:type="xs:int">13</b><c xmlns="" xsi:type="xs:float">3.14</c></myFunction>
>>>

Message可以和MethodDescriptor对象结合,用来表示一个SOAP方法调用中输入和输出的消息。方法介绍的作用并不是简单的保存方法名称,输入和输出的消息,它还可以和简单的soap客户端一起被用于远程SOAP服务的调用。The Axis stock quote 客户端的示例可以这样写:

in_message = Message('getPrice',[('symbol',String)],ns='http://quickstart.samples/xsd')
out_message = Message('getPriceResponse',[('return',Float)],ns='http://quickstart.samples/xsd')

method = MethodDescriptor('getPrice',in_message,out_message)
client = SimpleSoapClient('localhost:8080','/axis2/services/StockQuoteService',method)
print client('IBM')

消息名称也可以在ElementTree QName 语法中指定(比如 “{http://quickstart.samples/xsd}getPrice” )并且可以为消息指定可选的“type”关键词,该关键词在WSDL生成时被使用。


Hooks(轮子)

下面的例子是HelloWorld示例的加强版,它使用‘hooks’服务将横截行为运用于该服务。在本例中,hooks服务用于收集方法执行和整个调用期间的性能信息,包括序列化和反序列化。这些可用的轮子包括:

on_call()

	This is the first thing called in the service

soaplib.core.service.DefinitionBase.on_wsdl()

	Called before the wsdl is requested

on_wsdl_exception()

	Called after an exception was thrown when generating the wsdl (shouldn’t happen very much)

on_method_call()

	Called right before the service method is executed

on_method_return()

	Called right after the service method is executed

on_method_exception_object()

	Called when an exception occurred in a service method before the exception is serialized.

on_method_exception_xml()

	Called after an exception occurred in either the service method or in serialization

on_return()

	This is the very last thing called before the wsgi app exits

这些函数可以被轻易的应用于服务中所有的方法的cross-cutting functionality,从而完成数据库事务管理,日志记录和测量性能。此示例中也使用了threadlocal请求(soaplib.core.wsgi_soap.request)对象来保存请求的数据点。

from soaplib.core.service import rpc, DefinitionBase
from soaplib.core.model.primitive import String, Integer
from soaplib.core.model.clazz import Array

from time import time

class HelloWorldService(DefinitionBase):

    @soap(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

    def on_call(self,environ):
        request.additional['call_start'] = time()

    def on_method_exec(self,environ,body,py_params,soap_params):
        request.additional['method_start'] = time()

    def on_results(self,environ,py_results,soap_results,http_headers):
        request.additional['method_end'] = time()

    def on_return(self,environ,returnString):
        call_start = request.additional['call_start']
        call_end = time()
        method_start = request.additional['method_start']
        method_end = request.additional['method_end']

        print 'Method took [%s] - total execution time[%s]'%(method_end-method_start,call_end-call_start)


if __name__=='__main__':
    from wsgiref.simple_server import make_server
    server = make_server('localhost', 7789, Application([HelloWorldService]))
    server.serve_forever()

运行这个程序:

Method took [0.000195980072021] - total execution time[0.00652194023132]
Method took [0.000250101089478] - total execution time[0.00567507743835]
Method took [0.000144004821777] - total execution time[0.00521206855774]
Method took [0.000141859054565] - total execution time[0.00512409210205]
Method took [0.00377607345581] - total execution time[0.00511980056763]
Method took [0.00118803977966] - total execution time[0.00673604011536]
Method took [0.000146150588989] - total execution time[0.00157499313354]
Method took [0.0231170654297] - total execution time[0.0245010852814]
Method took [0.000166893005371] - total execution time[0.01802110672]

这也许有助于在进程中找到瓶颈,但此技术也可以被用于提交/回滚事务,或为服务中的所有方法提供设置/拆卸操作。


Axis Interoperability(引擎互用性)

Apache Axis 项目是目前最流行的SOAP实现引擎之一,所以对于Soaplib来说,能够轻松的使用它是非常重要的。作为soaplib的子项目,axis_soaplib测试套件被创造出来作为soaplib和Axis能够了解彼此的保证。此测试使用Apache Ant来运行并生成客户端使用wsdl2java命令的java类。


怎样运行测试

  • 从soaplib目录中运行soaplib的interop service。
    % python tests/interop_service.py
  • 修改axis_soaplib中的build.xml来映射正确的URL以及axis库的路径
  • 跑这个测试
% ant Buildfile: build.xml
wsdl2java:
compile:
	[javac] Compiling 9 source files [javac] Note: 
	Some input files use unchecked or unsafe 
	operations. [javac] Note: Recompile with -
	Xlint:unchecked for details.
test:
	[java] Results ——————————————- 
	[java] Total Tests 16 [java] Failures 0

BUILD SUCCESSFUL Total time: 7 seconds

调整方法参数

在python中,函数的参数包含可选和强制两类。SOAP也允许重复的参数;这是使用SOAP对象的**customize()**方法完成的。
一个SOAP方法的参数具有许多与基数相关的特性。

  • min_occurs:定义该参数的最小出现次数;设置为0,将该参数标记为可选类型。
  • max_occurs:定义该参数的最大出现次数;如果大于1,该参数将作为列表传递。
  • nillable:True或者False,甚至一个’Null’值(一个空的XML元素)可以被该参数传递;该值将转化为Python中的NONE值。
    示例
class MyService(DefinitionBase):

  @soap(
      String.customize(min_occurs=1, max_occurs=1, nillable=False),
      String.customize(min_occurs=0, max_occurs="unbounded", nillable=True),
      String.customize(min_occurs=4, max_occurs=10, nillable=False)
  )
  def my_method(self, mandatory_string, list_of_strings, a_few_strings):
      pass
  • 第一个参数必须存在,不能为NONE;它还必须是一个空的字符串。
  • 第二个参数可以是字符串列表,其中一些值可以是None(例如 :[‘foo’,None,‘bar’,None,None,‘baz’])
  • 第三个参数是一个字符串列表,包括4到10个items。不能包含None:[‘foo’,‘bar’,‘baz’,‘foo’,’’,‘foo’]

基元

对于基元类型,不需要调用customize()方法:

# These are equivalent
String(min_occurs=0, max_occurs=1, nillable=False)
String.customize(min_occurs=0, max_occurs=1, nillable=False)

自定义类必须使用customize()(继承于ClassModel)。


报错

在SOAP的WSDL中,errors被视为Faults。这些可以在每个基于soaplib方法中使用Fault类定义。
因为这个类是异常的子类,所以Fault可以像任何标准错误一样被触发。
对于一个特定的方法,它的有效错误被定义于它定义中的**_faults**关键词参数中:

from soaplib.core.model import exception
from soaplib.core import service

class MyFault(exception.Fault):
    __namespace__ = 'faults'

class MyService(service.DefinitionBase):

    @soap(String, _faults=(MyFault,))
    def MyMethod(self, name):
        if name != 'foobar':
            raise MyFault('Invalid name: %s' % name)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值