在GAE上搭建XML-RPC服务

标签:Google App Engine, Python

前些日子有人问Doodle是否支持Windows Live Writer,我只是回答了不支持而已。原因很简单,我的Windows XP很奇怪,几次装Live Writer都失败,对微软已经绝望了。
不过现在已经转移到Mac OS X上了,装软件没这么奇葩的兼容性问题,于是就研究起weblog publishing API来。

博客发布API大致有2类:一类是基于XML-RPC服务的,包括Blogger API、MetaWeblog API、MovableType API和WordPress API等;另一类则是Atom Publishing Protocol。
后者虽然技术上比前者强大,但较为复杂(例如认证是基于HTTP的认证机制),而且由于出现得较晚,支持它的也比较少。当然,对我来说更重要的是文档很难找,因为这个协议并不只用于blog,一些细节上的东西就不知道怎么搜索了。
于是比较了1天后,还是选择了XML-RPC。简单来说,它就是用XML封装方法名和各个参数,POST给XML-RPC服务器,再由服务器返回XML封装的方法运行结果。至于各种API,实际上只是方法和参数的差异,原理都是一致的。

下面举个提供加法的XML-RPC例子。

先是客户端,它需要POST这样一个XML给HTTP服务器:
<?xml version="1.0"?>
<methodCall>
	<methodName>add</methodName>
	<params>
		<param>
			<value><int>2</int></value>
		</param>
		<param>
			<value><int>3</int></value>
		</param>
	</params>
</methodCall>
其中methodName是方法名,它还有2个参数,类型为int,值分别为2和3。至于可用的类型,可以参考维基百科的XML-RPC条目,我发现官方文档反而不全。

而服务器接到这个请求后,则应该找到add这个方法,计算出结果为5,然后输出这样一个XML:
<?xml version='1.0'?>
<methodResponse>
	<params>
		<param>
			<value><int>5</int></value>
		</param>
	</params>
</methodResponse>

虽然这是一个很简单的处理过程,不过和XML打交道都是很烦的,要去提取方法名和各种参数,还得构造一个XML,想起来就觉得繁琐。
好在搜索了一下,发现Python内置了关于XML-RPC的标准库(SimpleXMLRPCServer和xmlrpclib),顿时觉得Pythoner太幸福了。

这里我就以一个实现加减法的XML-RPC服务为例了。由于最终是搭建在Google App Engine上,所以并不能直接使用SimpleXMLRPCServer类,而是使用它的父类SimpleXMLRPCDispatcher。
话说自带的文档里并没有提到这个玩意,我还是在Epydoc文档里找到的。而且这个文档也没什么用,最终还是直接看源码去了…
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
from google.appengine.ext.webapp import util
import yui

class XMLRPCDispatcher(SimpleXMLRPCDispatcher):
	def __init__(self, funcs):
		SimpleXMLRPCDispatcher.__init__(self, True, 'utf-8')
		self.funcs = funcs
		self.register_introspection_functions()

dispatcher = XMLRPCDispatcher({
	'add': lambda x, y: x + y,
	'sub': lambda x, y: x - y
})

class RpcHandler(yui.RequestHandler):
	def post(self):
		self.set_content_type('text/xml')
		response = dispatcher._marshaled_dispatch(self.request.body)
		self.write(response)

application = yui.WsgiApplication([
	('/rpc', RpcHandler)
])

def main():
	util.run_wsgi_app(application)

if __name__ == '__main__':
	main()
SimpleXMLRPCDispatcher对象的__init__()方法接收3个参数,第2个是方法名中是否允许带“.”号,第3个则是XML的编码了。
它的funcs属性是个字典,也就是方法名和方法之间的映射。
它的register_introspection_functions()方法则是根据funcs属性里的方法来生成system.listMethods、system.methodSignature和system.methodHelp这3个方法,主要就是对外公布自己可以提供哪些方法。
初始化并设置完它的方法后,就可以调用它的_marshaled_dispatch()方法了。这个方法接收一个XML字符串参数,自动解析并运行对应的方法,然后再把结果封装成XML返回。于是就可以不用直接和讨厌的XML打交道了~

服务端做好后就可以用客户端来测试了,这玩意就更简单了。构造一个xmlrpclib.Server对象,然后直接调用它的方法即可:
import xmlrpclib

s = xmlrpclib.Server('http://localhost:8080/rpc')
print s.add(2, 3)
print s.sub(5, 2)
print s.system.listMethods()
要注意的是,构造Server对象时,如果没给出路径,默认的路径并不是"/",而是"/RPC2"。
而如上所述,system.listMethods()方法是由register_introspection_functions()方法自动生成的,用于列出所有方法。

最终,这个例子的结果如下:
5
3
['add', 'sub', 'system.listMethods', 'system.methodHelp', 'system.methodSignature']

如果只想生成XML的话,用xmlrpclib.dumps()函数就行了。好在GAE上可以直接用xmlrpclib.Server,也就无需自己调用urlfetch了(除非需要异步urlfetch)。

最后顺带一提,上面提到的博客发布API中,我觉得最简洁好用的是MetaWeblog API,不过缺了删除日志和列出标签的API;WordPress API是最丰富的,可惜很多功能用不上。感觉至少得实现3种API来进行组合,才能提供完整好用的XML-RPC服务。

5条评论 你不来一发么↓ 顺序排列 倒序排列

    向下滚动可载入更多评论,或者点这里禁止自动加载

    想说点什么呢?