Python中SOAP项目的介绍及其在web开发中的应用
SOAP.py客户机和服务器
SOAP.py包含的是一些基本的东西。没有Web服务描述语言(WebServicesDescriptionLanguage,WSDL)或者任何其它附加的东西,只有用Python实现的SOAP客户机和服务器的透明支持。甚至这个包中的一个很好的功能也只是与基础架构相关:SOAP.py支持安全套接字层(SSL)用于加密的SOAP传输。为使用这个功能,您必须安装M2Crypto,M2Crypto是一个库,包含各种加密工具和格式,从RSA和DSA到HTTPs、S/MIME等等。在这一部分,我们不准备讨论SOAP.py的SSL支持。
SOAP操作摘要
目前为止,SOAP实用程序好象仍是比较流行的使用Python的开放源代码活动。下面是该项目的纲要以及它们目前的状态。首先,参与者:
- 4SuiteSOAP,由Fourthought管理
- SOAPy,由AdamElman管理
- SOAP.py,Python项目的一个Web服务项目
- soaplib,由SecretLabs管理
- Orchard,由KenMacLeod管理
- PySOAP,由DaveWarner管理
4SuiteSOAP是我们自己的实现,我们在本专栏的前面三部分中使用过(请参阅参考资料以获得它的链接)。它目前仍在开发中。
SOAPy是在2001年4月公布的,目前处于alpha的预备阶段,但现在好象停止开发了。
SOAP.py开发被冻结了。SOAP.py这个项目是由actzero公司赞助的,而actzero却不再从事这一行业了。正在邀请自愿开发/维护SOAP.py的组织。
soaplib的开发好象也延缓了,考虑到SecretLabs这段时间所承担的大量工作,或许就可以理解为什么会这样了。这个瑞典的公司是由FredrikLundh掌管的,他在Python圈内是出名的“工作狂”,同时也是PythonAssociation董事会的一名成员。SecretLabs还开发PythonWare(Python的一个核心和重要的附加模块);PythonWorks(一个领先的PythonIDE);PythonImagingLibrary和许多其它好东西(日常Python-URLWeb日志就是其中的一部分)。
Orchard是一个数据管理框架,基本上是一种用一个公共接口管理不同数据格式的方法。它实现了一个SOAP客户机作为在远程过程调用中向SOAP服务器发送Orchard数据项的基本方法(被称为节点)。
PySOAP这个项目主要是想作为DaveWarner的Church管理套件的一部分,但它还从没发行过任何文件,好象是一个毫无生气的项目。
安装
开始先下载分发包(在写这篇文章的时候,SOAPpy0.9.7是最新的分发包),把文件解包,转到结果目录,并把文件SOAP.py复制到自己倾向的位置。当然,这个“倾向”就是需要技巧的地方。由于这些SOAPlib中有很多都使用大小写组合不同的“soap.py”作为模块名,所以大家一定要小心。当然,UNIX用户只需关心大小写是否精确匹配,但对于Windows用户来说,甚至“SOAP.py”和“soap.py”之间的冲突也会带来麻烦。Orchard的SOAP.py也有一个容易发生冲突的名称,但它有可能避开所有的问题,因为它的模块聪明地放在了Orchard包中。
上面的内容简言之就是建议您确保安装所有的PythonSOAP模块时都使用与众不同的包名称。在我们的案例中,我们在PYTHONPATH中发现了一个合适的目录并创建了一个WebServices包,把SOAP.py放在了这个包中。因此,在Linux中:
$mkdir~/lib/python/WebServices $touch~/lib/python/WebServices/__init__.py $cpSOAPpy097/SOAP.py~/lib/python/WebServices
请注意很重要的第二条命令,它将生成一个__init__.py文件,这个文件将WebServices目录标志为Python包。如果您需要把这些代码打包成Windows版本,您可能希望向空文件中输入一些注释,因为一些Windows工具不创建空文件。
您已深入主题了
对于公开提供的SOAP服务器,早已经有了好几个活动的注册中心。最流行的可能是XMethods。当然,它也是一个相当有趣的指导,通过它我们可以了解SOAP的实际状况,而不要听它的吹嘘。这里的大多数公共Web服务仍然只是一些无关紧要的东西,几乎不值得我们勇敢的新模型多费口舌,但那是另一回事了。实际上,我们将选择一个公共服务来演示和测试如何把SOAP.py作为SOAP客户机使用。
或者,我们可以试试。作者尝试的第一个服务,卫生保健提供者定位器,在遇到下列报错消息时显示SOAP互操作性的当前状态中的陷阱:
WebServices.SOAP.faultType:<Faultsoap:Client: Serverdidnotrecognizethevalueof HTTPHeaderSOAPAction:"".>
哦。SOAPAction是一个HTTP头,应该是用来标记被访问服务的。它是SOAP请求中必需的头,但即便是设置了所需的头(只是一对空的双引号)后,上面的错误仍然存在。作者发现大多数MSSOAP实现都存在这个问题。在试遍了这些服务后,我们断定,Delphi实现好象与SOAP.py合作得最好,但在试服务时—即使是用Delphi实现时,也返回复杂的类型,比如列表,SOAP.py无法使用它们,返回不带数据的WebServices.SOAP.typedArrayType实例。
最后,作者选择了一个相当合适的Web服务,该服务返回漫画《丁丁历险记》中的人物Haddock船长常用的骂人语言(是的,大多数Web服务都是这样)。清单1(curse.py)就是这个程序。
清单1:访问Curse生成器SOAP服务的SOAP.py程序
#!/usr/bin/envpython #http://xmethods.net/detail.html?id=175 importsys #ImporttheSOAP.pymachinery fromWebServicesimportSOAP remote=SOAP.SOAPProxy( "http://www.tankebolaget.se/scripts/Haddock.exe/soap/IHaddock", namespace="urn:HaddockIntf-IHaddock", soapaction="urn:HaddockIntf-IHaddock#Curse" ) try: lang=sys.argv[1] exceptIndexError: lang="us" result=remote.Curse(LangCode=lang) print"WhatcaptainHaddockhadtosay:"%s""%result
把一切综合在一起
导入库后,我们将设置代理对象remote。这个对象将方法调用转换为远程SOAP消息。它的初始化器使用管理远程请求的关键参数:服务器的URI(被称为“端点”)、请求元素的XML名称空间(通过它,SOAP-as-RPC将口头承诺变成XML基础)和SOAPAction头值。
接下来,我们将确定方法参数,对于这个Web服务来说,方法参数只是Haddock骂人的语言,瑞典语(“se”)或英语(奇怪的是,是“us”而不是“en”)。
最后,我们调用名称正确的方法,代理对象的Curse进行SOAP调用,然后打印出结果。下面的会话演示了对该程序的使用:
$pythoncurse.py WhatcaptainHaddockhadtosay:"EctoplasmicByproduct!"
我们自己的SOAP服务器
用SOAP.py实现SOAP服务器相当容易。作为一个示例,我们将仿建字段,还要实现一个很常见的服务:一个程序,给出年份和月份,它将以字符串的形式打印出日历。它的程序服务器是清单2(calendar-ws.py)。
清单2:实现日历服务器的SOAP.py程序
#!/usr/bin/envpython importsys,calendar #ImporttheSOAP.pymachinery fromWebServicesimportSOAP CAL_NS="http://uche.ogbuji.net/eg/ws/simple-cal" classCalendar: defgetMonth(self,year,month): returncalendar.month(year,month) defgetYear(self,year): returncalendar.calendar(year) server=SOAP.SOAPServer(("localhost",8888)) cal=Calendar() server.registerObject(cal,CAL_NS) print"Startingserver..." server.serve_forever()
进行过必要的导入后,我们为自己的服务器定义SOAP请求元素期望的名称空间(CAL_NS)。接下来我们定义实现所有方法的类,这些方法将被公开为SOAP方法。大家也可以把单个函数作为SOAP方法注册,但使用类方法是最灵活的,特别是当您想管理调用间的状态时。这个Calendar类定义了一个方法getMonth,该方法使用Python的内置日历模块在文本表单中返回月度日历,同时它还定义了另一个返回整年日历的方法。
然后创建SOAP服务器框架的一个实例,这个实例还带有侦听端口8888的指令。我们还必须创建Calendar类的一个实例,这个实例在下一行中被注册用来处理SOAP消息,同时为其指出相关的名称空间。最后,我们调用serve_forever方法,该方法直到进程终止才返回。
为运行服务器,请打开另一个命令shell并执行pythoncalendar-ws.py。执行结束时使用ctrl-C杀死进程。
我们本来可以用也是用SOAP.py写的客户机测试服务器,但那太显而易见了。我们还是用低级Python编写客户机把SOAP响应作为XML字符串来构建,并发送一条HTTP消息。这个程序(testcal.py)在清单3中。
清单3:用Python核心库写的访问日历服务的客户机
importsys,httplib SERVER_ADDR="127.0.0.1" SERVER_PORT=8888 CAL_NS="http://uche.ogbuji.net/ws/eg/simple-cal" BODY_TEMPLATE="""<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s="http://uche.ogbuji.net/eg/ws/simple-cal" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" > <SOAP-ENV:Body> <s:getMonth> <yearxsi:type="xsd:integer">%s</year> <monthxsi:type="xsd:integer">%s</month> </s:getMonth> </SOAP-ENV:Body> </SOAP-ENV:Envelope>""" defGetMonth(): year=2001 month=12 body=BODY_TEMPLATE%(year,month) blen=len(body) requestor=httplib.HTTP(SERVER_ADDR,SERVER_PORT) requestor.putrequest("POST","cal-server") requestor.putheader("Host",SERVER_ADDR) requestor.putheader("Content-Type","text/plain;charset="utf-8"") requestor.putheader("Contentreply_body=requestor.getfi-Length",str(blen)) requestor.putheader("SOAPAction","http://uche.ogbuji.net/eg/ws/simple-car") requestor.endheaders() requestor.send(body) (status_code,message,reply_headers)=requestor.getreply() le().read() print"statuscode:",status_code print"statusmessage:",message print"HTTPreplybody:\n",reply_body if__name__=="__main__": GetMonth()
下面的会话演示了这个测试的运行情况。
$pythontestcal.py statuscode:200 statusmessage:OK HTTPreplybody: <?xmlversion="1.0"encoding="UTF-8"?> <SOAP-ENV:EnvelopeSOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema"xmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SO AP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <getMonthResponseSOAP-ENC:root="1"> <Resultxsi:type="xsd:string">December2001 MoTuWeThFrSaSu 12 3456789 10111213141516 17181920212223 24252627282930 31 </Result> </getMonthResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
仔细审查的字节
如果您查找行self.debug=0并把“0”改为“1”(这是SOAP.py版本0.9.7中的第210行),有一件要注意的事情是您可以获得被交换的实际SOAP消息的详细信息和用于调试与跟踪的其它关键数据,这对您很有用。作为示例,下面提供了一个会话,它是打开了调试信息显示开关的以前的curses.py程序的一个会话:
$pythoncurse.py ***OutgoingHTTPheaders********************************************** POST/scripts/Haddock.exe/soap/IHaddockHTTP/1.0 Host:www.tankebolaget.se User-agent:SOAP.py0.9.7(actzero.com) Content-type:text/xml;charset="UTF-8" Content-length:523 SOAPAction:"urn:HaddockIntf-IHaddock#Curse" ************************************************************************ ***OutgoingSOAP****************************************************** <?xmlversion="1.0"encoding="UTF-8"?> <SOAP-ENV:EnvelopeSOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema"xmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SO AP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <ns1:Cursexmlns:ns1="urn:HaddockIntf-IHaddock"SOAP-ENC:root="1"> <LangCodexsi:type="xsd:string">us</LangCode> </ns1:Curse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> ************************************************************************ ***IncomingHTTPheaders********************************************** HTTP/1.?200OK Server:Microsoft-IIS/5.0 Date:Tue,11Sep200116:40:19GMT Content-Type:text/xml Content-Length:528 Content: ************************************************************************ ***IncomingSOAP****************************************************** <?xmlversion="1.0"encoding="UTF-8"?><SOAP-ENV:Envelopexmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"xmlns:SOAP-ENC="http://schemas.xml soap.org/soap/encoding/"><SOAP-ENV:Body><NS1:CurseResponsexmlns:NS1="urn:HaddockIntf- IHaddock"SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><NS1:return xsi:type="xsd:string">Anacoluthons!</NS1:return></NS1:CurseRespon se></SOAP-ENV:Body></SOAP-ENV:Envelope> ************************************************************************ WhatcaptainHaddockhadtosay:"Anacoluthons!"
为进行比较,您可以在带有下列代码的旧的Python脚本或程序中获得相同的信息:
importcalendar returncalendar.month(2001,10)
SOAP.py总结
我们已经注意到了,虽然SOAP.py的互操作性还存在一些问题,但可用的调试工具可望提供帮助。